delimit-cli 1.0.0 → 2.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.
Files changed (95) hide show
  1. package/.github/workflows/api-governance.yml +24 -0
  2. package/README.md +57 -115
  3. package/adapters/codex-skill.js +87 -0
  4. package/adapters/cursor-extension.js +190 -0
  5. package/adapters/gemini-action.js +93 -0
  6. package/adapters/openai-function.js +112 -0
  7. package/adapters/xai-plugin.js +151 -0
  8. package/bin/delimit-cli.js +921 -0
  9. package/bin/delimit.js +237 -1
  10. package/delimit.yml +19 -0
  11. package/hooks/evidence-status.sh +12 -0
  12. package/hooks/git/commit-msg +4 -0
  13. package/hooks/git/pre-commit +4 -0
  14. package/hooks/git/pre-push +4 -0
  15. package/hooks/install-hooks.sh +583 -0
  16. package/hooks/message-auth-hook.js +9 -0
  17. package/hooks/message-governance-hook.js +9 -0
  18. package/hooks/models/claude-post.js +4 -0
  19. package/hooks/models/claude-pre.js +4 -0
  20. package/hooks/models/codex-post.js +4 -0
  21. package/hooks/models/codex-pre.js +4 -0
  22. package/hooks/models/cursor-post.js +4 -0
  23. package/hooks/models/cursor-pre.js +4 -0
  24. package/hooks/models/gemini-post.js +4 -0
  25. package/hooks/models/gemini-pre.js +4 -0
  26. package/hooks/models/openai-post.js +4 -0
  27. package/hooks/models/openai-pre.js +4 -0
  28. package/hooks/models/windsurf-post.js +4 -0
  29. package/hooks/models/windsurf-pre.js +4 -0
  30. package/hooks/models/xai-post.js +4 -0
  31. package/hooks/models/xai-pre.js +4 -0
  32. package/hooks/post-bash-hook.js +13 -0
  33. package/hooks/post-mcp-hook.js +13 -0
  34. package/hooks/post-response-hook.js +4 -0
  35. package/hooks/post-tool-hook.js +126 -0
  36. package/hooks/post-write-hook.js +13 -0
  37. package/hooks/pre-bash-hook.js +30 -0
  38. package/hooks/pre-mcp-hook.js +13 -0
  39. package/hooks/pre-read-hook.js +13 -0
  40. package/hooks/pre-search-hook.js +13 -0
  41. package/hooks/pre-submit-hook.js +4 -0
  42. package/hooks/pre-task-hook.js +13 -0
  43. package/hooks/pre-tool-hook.js +121 -0
  44. package/hooks/pre-web-hook.js +13 -0
  45. package/hooks/pre-write-hook.js +31 -0
  46. package/hooks/test-hooks.sh +12 -0
  47. package/hooks/update-delimit.sh +6 -0
  48. package/lib/agent.js +509 -0
  49. package/lib/api-engine.js +156 -0
  50. package/lib/auth-setup.js +891 -0
  51. package/lib/decision-engine.js +474 -0
  52. package/lib/hooks-installer.js +416 -0
  53. package/lib/platform-adapters.js +353 -0
  54. package/lib/proxy-handler.js +114 -0
  55. package/package.json +38 -30
  56. package/scripts/infect.js +128 -0
  57. package/test-decision-engine.js +181 -0
  58. package/test-hook.js +27 -0
  59. package/dist/commands/validate.d.ts +0 -2
  60. package/dist/commands/validate.d.ts.map +0 -1
  61. package/dist/commands/validate.js +0 -106
  62. package/dist/commands/validate.js.map +0 -1
  63. package/dist/index.d.ts +0 -3
  64. package/dist/index.d.ts.map +0 -1
  65. package/dist/index.js +0 -71
  66. package/dist/index.js.map +0 -1
  67. package/dist/types/index.d.ts +0 -39
  68. package/dist/types/index.d.ts.map +0 -1
  69. package/dist/types/index.js +0 -3
  70. package/dist/types/index.js.map +0 -1
  71. package/dist/utils/api.d.ts +0 -3
  72. package/dist/utils/api.d.ts.map +0 -1
  73. package/dist/utils/api.js +0 -64
  74. package/dist/utils/api.js.map +0 -1
  75. package/dist/utils/file.d.ts +0 -7
  76. package/dist/utils/file.d.ts.map +0 -1
  77. package/dist/utils/file.js +0 -69
  78. package/dist/utils/file.js.map +0 -1
  79. package/dist/utils/logger.d.ts +0 -14
  80. package/dist/utils/logger.d.ts.map +0 -1
  81. package/dist/utils/logger.js +0 -28
  82. package/dist/utils/logger.js.map +0 -1
  83. package/dist/utils/masker.d.ts +0 -14
  84. package/dist/utils/masker.d.ts.map +0 -1
  85. package/dist/utils/masker.js +0 -89
  86. package/dist/utils/masker.js.map +0 -1
  87. package/src/commands/validate.ts +0 -150
  88. package/src/index.ts +0 -80
  89. package/src/types/index.ts +0 -41
  90. package/src/utils/api.ts +0 -68
  91. package/src/utils/file.ts +0 -71
  92. package/src/utils/logger.ts +0 -27
  93. package/src/utils/masker.ts +0 -101
  94. package/test-sensitive.yaml +0 -109
  95. package/tsconfig.json +0 -23
@@ -0,0 +1,416 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Delimit Auto-Hooks Installer
5
+ * Automatically installs Delimit governance hooks for all AI models and tools
6
+ */
7
+
8
+ const fs = require('fs');
9
+ const path = require('path');
10
+ const { execSync } = require('child_process');
11
+ const chalk = require('chalk');
12
+
13
+ class DelimitHooksInstaller {
14
+ constructor() {
15
+ this.hooksDir = path.join(process.env.HOME, '.delimit', 'hooks');
16
+ this.aiToolsDir = path.join(process.env.HOME, '.delimit', 'ai-hooks');
17
+ this.mcpHooksDir = path.join(process.env.HOME, '.delimit', 'mcp-hooks');
18
+
19
+ // AI models and tools to hook
20
+ this.aiTools = {
21
+ 'claude': {
22
+ name: 'Claude (Anthropic)',
23
+ configPaths: [
24
+ '~/.claude.json',
25
+ '~/.config/claude/config.json'
26
+ ],
27
+ hookType: 'wrapper'
28
+ },
29
+ 'codex': {
30
+ name: 'GitHub Copilot/Codex',
31
+ configPaths: [
32
+ '~/.config/github-copilot',
33
+ '~/.codex/config.json'
34
+ ],
35
+ hookType: 'wrapper'
36
+ },
37
+ 'gemini': {
38
+ name: 'Google Gemini',
39
+ configPaths: [
40
+ '~/.config/gemini',
41
+ '~/.gemini/config.json'
42
+ ],
43
+ hookType: 'wrapper'
44
+ },
45
+ 'openai': {
46
+ name: 'OpenAI GPT',
47
+ configPaths: [
48
+ '~/.config/openai',
49
+ '~/.openai/config.json'
50
+ ],
51
+ hookType: 'wrapper'
52
+ },
53
+ 'cursor': {
54
+ name: 'Cursor IDE',
55
+ configPaths: [
56
+ '~/.cursor/config.json',
57
+ '~/.config/cursor'
58
+ ],
59
+ hookType: 'extension'
60
+ },
61
+ 'windsurf': {
62
+ name: 'Windsurf',
63
+ configPaths: [
64
+ '~/.windsurf/config.json'
65
+ ],
66
+ hookType: 'wrapper'
67
+ }
68
+ };
69
+
70
+ // Git hooks to install
71
+ this.gitHooks = [
72
+ 'pre-commit',
73
+ 'pre-push',
74
+ 'commit-msg',
75
+ 'pre-merge-commit',
76
+ 'prepare-commit-msg',
77
+ 'post-commit',
78
+ 'post-merge',
79
+ 'pre-rebase'
80
+ ];
81
+
82
+ // MCP hooks for governance integration
83
+ this.mcpHooks = [
84
+ 'pre-mcp-call',
85
+ 'post-mcp-call',
86
+ 'mcp-auth',
87
+ 'mcp-audit'
88
+ ];
89
+ }
90
+
91
+ async install() {
92
+ console.log(chalk.blue.bold('\nšŸ”µ Delimit Multi-Model Hooks Installer\n'));
93
+
94
+ // Create directories
95
+ this.ensureDirectories();
96
+
97
+ // Install Git hooks
98
+ console.log(chalk.yellow('šŸ“¦ Installing Git hooks...'));
99
+ await this.installGitHooks();
100
+
101
+ // Install AI tool hooks
102
+ console.log(chalk.yellow('šŸ¤– Installing AI model hooks...'));
103
+ await this.installAIToolHooks();
104
+
105
+ // Install MCP hooks
106
+ console.log(chalk.yellow('šŸ”— Installing MCP integration hooks...'));
107
+ await this.installMCPHooks();
108
+
109
+ // Configure Claude Code integration
110
+ console.log(chalk.yellow('⚔ Configuring Claude Code integration...'));
111
+ await this.configureClaudeCode();
112
+
113
+ // Setup environment
114
+ console.log(chalk.yellow('šŸŒ Setting up environment...'));
115
+ await this.setupEnvironment();
116
+
117
+ console.log(chalk.green.bold('\nāœ… Delimit hooks installed successfully!\n'));
118
+ this.printSummary();
119
+ }
120
+
121
+ ensureDirectories() {
122
+ const dirs = [this.hooksDir, this.aiToolsDir, this.mcpHooksDir];
123
+ dirs.forEach(dir => {
124
+ if (!fs.existsSync(dir)) {
125
+ fs.mkdirSync(dir, { recursive: true });
126
+ }
127
+ });
128
+ }
129
+
130
+ async installGitHooks() {
131
+ for (const hook of this.gitHooks) {
132
+ const hookPath = path.join(this.hooksDir, hook);
133
+ const hookContent = this.generateGitHook(hook);
134
+
135
+ fs.writeFileSync(hookPath, hookContent);
136
+ fs.chmodSync(hookPath, '755');
137
+ console.log(chalk.green(` āœ“ ${hook}`));
138
+ }
139
+
140
+ // Set global Git hooks path
141
+ try {
142
+ execSync(`git config --global core.hooksPath ${this.hooksDir}`);
143
+ console.log(chalk.green(' āœ“ Git global hooks path configured'));
144
+ } catch (e) {
145
+ console.log(chalk.yellow(' ⚠ Could not set global Git hooks (may need sudo)'));
146
+ }
147
+ }
148
+
149
+ async installAIToolHooks() {
150
+ for (const [tool, config] of Object.entries(this.aiTools)) {
151
+ const hookPath = path.join(this.aiToolsDir, tool);
152
+ const wrapperPath = path.join(this.aiToolsDir, `${tool}-wrapper`);
153
+
154
+ // Check if tool is installed
155
+ const isInstalled = this.checkToolInstalled(tool);
156
+
157
+ if (isInstalled) {
158
+ // Create wrapper script
159
+ const wrapperContent = this.generateAIToolWrapper(tool, config);
160
+ fs.writeFileSync(wrapperPath, wrapperContent);
161
+ fs.chmodSync(wrapperPath, '755');
162
+
163
+ // Create hook configuration
164
+ const hookContent = this.generateAIToolHook(tool, config);
165
+ fs.writeFileSync(hookPath, hookContent);
166
+ fs.chmodSync(hookPath, '755');
167
+
168
+ console.log(chalk.green(` āœ“ ${config.name}`));
169
+ } else {
170
+ console.log(chalk.gray(` - ${config.name} (not installed)`));
171
+ }
172
+ }
173
+ }
174
+
175
+ async installMCPHooks() {
176
+ for (const hook of this.mcpHooks) {
177
+ const hookPath = path.join(this.mcpHooksDir, hook);
178
+ const hookContent = this.generateMCPHook(hook);
179
+
180
+ fs.writeFileSync(hookPath, hookContent);
181
+ fs.chmodSync(hookPath, '755');
182
+ console.log(chalk.green(` āœ“ ${hook}`));
183
+ }
184
+ }
185
+
186
+ async configureClaudeCode() {
187
+ const claudeConfigPath = path.join(process.env.HOME, '.claude.json');
188
+
189
+ if (fs.existsSync(claudeConfigPath)) {
190
+ try {
191
+ const config = JSON.parse(fs.readFileSync(claudeConfigPath, 'utf8'));
192
+
193
+ // Add Delimit governance hooks
194
+ if (!config.hooks) {
195
+ config.hooks = {};
196
+ }
197
+
198
+ config.hooks.preCommand = path.join(this.mcpHooksDir, 'pre-mcp-call');
199
+ config.hooks.postCommand = path.join(this.mcpHooksDir, 'post-mcp-call');
200
+ config.hooks.authentication = path.join(this.mcpHooksDir, 'mcp-auth');
201
+ config.hooks.audit = path.join(this.mcpHooksDir, 'mcp-audit');
202
+
203
+ // Add Delimit governance settings
204
+ config.delimitGovernance = {
205
+ enabled: true,
206
+ agent: 'http://127.0.0.1:7823',
207
+ mode: 'auto',
208
+ hooks: this.mcpHooks.map(h => path.join(this.mcpHooksDir, h))
209
+ };
210
+
211
+ fs.writeFileSync(claudeConfigPath, JSON.stringify(config, null, 2));
212
+ console.log(chalk.green(' āœ“ Claude Code configuration updated'));
213
+ } catch (e) {
214
+ console.log(chalk.yellow(' ⚠ Could not update Claude Code config'));
215
+ }
216
+ }
217
+ }
218
+
219
+ async setupEnvironment() {
220
+ const envFile = path.join(process.env.HOME, '.delimit', 'env');
221
+ const envContent = `
222
+ # Delimit Governance Environment
223
+ export DELIMIT_HOOKS_DIR="${this.hooksDir}"
224
+ export DELIMIT_AI_HOOKS_DIR="${this.aiToolsDir}"
225
+ export DELIMIT_MCP_HOOKS_DIR="${this.mcpHooksDir}"
226
+ export DELIMIT_AGENT_URL="http://127.0.0.1:7823"
227
+ export DELIMIT_GOVERNANCE_ENABLED=true
228
+
229
+ # AI Tool Governance
230
+ export CLAUDE_GOVERNANCE_HOOK="${path.join(this.aiToolsDir, 'claude')}"
231
+ export CODEX_GOVERNANCE_HOOK="${path.join(this.aiToolsDir, 'codex')}"
232
+ export GEMINI_GOVERNANCE_HOOK="${path.join(this.aiToolsDir, 'gemini')}"
233
+ `;
234
+
235
+ fs.writeFileSync(envFile, envContent);
236
+ console.log(chalk.green(' āœ“ Environment configuration created'));
237
+
238
+ // Add to bashrc if not already present
239
+ const bashrcPath = path.join(process.env.HOME, '.bashrc');
240
+ const sourceLine = 'source ~/.delimit/env';
241
+
242
+ if (fs.existsSync(bashrcPath)) {
243
+ const bashrc = fs.readFileSync(bashrcPath, 'utf8');
244
+ if (!bashrc.includes(sourceLine)) {
245
+ fs.appendFileSync(bashrcPath, `\n# Delimit Governance Environment\n${sourceLine}\n`);
246
+ console.log(chalk.green(' āœ“ Added to .bashrc'));
247
+ }
248
+ }
249
+ }
250
+
251
+ generateGitHook(hookName) {
252
+ return `#!/bin/sh
253
+ # Delimit Dynamic Governance Hook - ${hookName}
254
+ # Auto-generated by Delimit Hooks Installer
255
+
256
+ # Ensure agent is running
257
+ if ! curl -s http://127.0.0.1:7823/status > /dev/null 2>&1; then
258
+ echo "Starting Delimit Agent..."
259
+ nohup node /home/delimit/npm-delimit/lib/agent.js > /dev/null 2>&1 &
260
+ sleep 2
261
+ fi
262
+
263
+ # Execute governance check
264
+ node /home/delimit/npm-delimit/bin/delimit-cli.js hook ${hookName} "$@"
265
+ `;
266
+ }
267
+
268
+ generateAIToolWrapper(tool, config) {
269
+ return `#!/bin/sh
270
+ # Delimit Governance Wrapper for ${config.name}
271
+ # Auto-generated by Delimit Hooks Installer
272
+
273
+ # Log the invocation
274
+ echo "[\$(date '+%Y-%m-%d %H:%M:%S')] ${tool} invoked with args: $*" >> ~/.delimit/audit/${tool}.log
275
+
276
+ # Check governance before execution
277
+ node /home/delimit/npm-delimit/bin/delimit-cli.js proxy ${tool} "$@"
278
+
279
+ # If governance passes, execute the real tool
280
+ if [ $? -eq 0 ]; then
281
+ # Find and execute the original binary
282
+ ORIGINAL_PATH=\$(echo \$PATH | sed "s|$HOME/.delimit/shims:||g")
283
+ PATH="\$ORIGINAL_PATH" command ${tool} "$@"
284
+ else
285
+ echo "Governance check failed for ${tool}"
286
+ exit 1
287
+ fi
288
+ `;
289
+ }
290
+
291
+ generateAIToolHook(tool, config) {
292
+ return `#!/usr/bin/env node
293
+ // Delimit Governance Hook for ${config.name}
294
+ // Auto-generated by Delimit Hooks Installer
295
+
296
+ const { execSync } = require('child_process');
297
+ const path = require('path');
298
+
299
+ // Governance check
300
+ async function checkGovernance() {
301
+ try {
302
+ const result = execSync('curl -s http://127.0.0.1:7823/status');
303
+ return JSON.parse(result.toString());
304
+ } catch (e) {
305
+ return { mode: 'advisory' };
306
+ }
307
+ }
308
+
309
+ // Main execution
310
+ (async () => {
311
+ const governance = await checkGovernance();
312
+ console.log(\`[Delimit] ${config.name} governance mode: \${governance.mode}\`);
313
+
314
+ // Log invocation
315
+ const args = process.argv.slice(2).join(' ');
316
+ console.log(\`[Delimit] Command: ${tool} \${args}\`);
317
+
318
+ // Execute based on governance mode
319
+ if (governance.mode === 'enforce') {
320
+ console.log('[Delimit] Enforce mode - checking permissions...');
321
+ // Add enforce logic here
322
+ }
323
+ })();
324
+ `;
325
+ }
326
+
327
+ generateMCPHook(hookName) {
328
+ return `#!/usr/bin/env node
329
+ // Delimit MCP Integration Hook - ${hookName}
330
+ // Auto-generated by Delimit Hooks Installer
331
+
332
+ const axios = require('axios');
333
+ const fs = require('fs');
334
+ const path = require('path');
335
+
336
+ async function ${hookName.replace(/-/g, '_')}(context) {
337
+ const agentUrl = process.env.DELIMIT_AGENT_URL || 'http://127.0.0.1:7823';
338
+
339
+ try {
340
+ // Send governance check
341
+ const response = await axios.post(\`\${agentUrl}/evaluate\`, {
342
+ hook: '${hookName}',
343
+ timestamp: new Date().toISOString(),
344
+ ...context
345
+ });
346
+
347
+ // Log to audit trail
348
+ const auditLog = path.join(process.env.HOME, '.delimit', 'audit', 'mcp.jsonl');
349
+ const logEntry = JSON.stringify({
350
+ hook: '${hookName}',
351
+ timestamp: new Date().toISOString(),
352
+ decision: response.data,
353
+ context
354
+ }) + '\\n';
355
+
356
+ fs.appendFileSync(auditLog, logEntry);
357
+
358
+ return response.data;
359
+ } catch (error) {
360
+ console.error('[Delimit] MCP hook error:', error.message);
361
+ return { action: 'allow', mode: 'advisory' };
362
+ }
363
+ }
364
+
365
+ // Export for use as module
366
+ module.exports = { ${hookName.replace(/-/g, '_')} };
367
+
368
+ // Execute if called directly
369
+ if (require.main === module) {
370
+ const context = {
371
+ args: process.argv.slice(2),
372
+ env: process.env,
373
+ pwd: process.cwd()
374
+ };
375
+
376
+ ${hookName.replace(/-/g, '_')}(context).then(result => {
377
+ console.log(JSON.stringify(result));
378
+ process.exit(result.action === 'block' ? 1 : 0);
379
+ });
380
+ }
381
+ `;
382
+ }
383
+
384
+ checkToolInstalled(tool) {
385
+ try {
386
+ execSync(`which ${tool}`, { stdio: 'ignore' });
387
+ return true;
388
+ } catch {
389
+ return false;
390
+ }
391
+ }
392
+
393
+ printSummary() {
394
+ console.log(chalk.cyan('\nšŸ“‹ Installation Summary:'));
395
+ console.log(chalk.white(` • Git hooks installed: ${this.gitHooks.length}`));
396
+ console.log(chalk.white(` • AI tool hooks configured: ${Object.keys(this.aiTools).length}`));
397
+ console.log(chalk.white(` • MCP integration hooks: ${this.mcpHooks.length}`));
398
+ console.log(chalk.white(` • Hooks directory: ${this.hooksDir}`));
399
+ console.log(chalk.white(` • Agent URL: http://127.0.0.1:7823`));
400
+
401
+ console.log(chalk.cyan('\nšŸŽÆ Next Steps:'));
402
+ console.log(chalk.white(' 1. Restart your shell to load environment'));
403
+ console.log(chalk.white(' 2. Run "delimit status" to verify installation'));
404
+ console.log(chalk.white(' 3. Test with "git commit" or AI tool commands'));
405
+ console.log(chalk.white(' 4. Configure policies in delimit.yml'));
406
+ }
407
+ }
408
+
409
+ // Export for use as module
410
+ module.exports = DelimitHooksInstaller;
411
+
412
+ // Run if executed directly
413
+ if (require.main === module) {
414
+ const installer = new DelimitHooksInstaller();
415
+ installer.install().catch(console.error);
416
+ }