delimit-cli 2.3.2 → 3.0.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/.dockerignore +7 -0
- package/.github/workflows/ci.yml +22 -0
- package/CHANGELOG.md +33 -0
- package/CODE_OF_CONDUCT.md +48 -0
- package/CONTRIBUTING.md +67 -0
- package/Dockerfile +9 -0
- package/LICENSE +21 -0
- package/README.md +51 -130
- package/SECURITY.md +42 -0
- package/adapters/codex-forge.js +107 -0
- package/adapters/codex-jamsons.js +142 -0
- package/adapters/codex-security.js +94 -0
- package/adapters/gemini-forge.js +120 -0
- package/adapters/gemini-jamsons.js +152 -0
- package/bin/delimit-cli.js +52 -2
- package/bin/delimit-setup.js +258 -0
- package/gateway/ai/backends/__init__.py +0 -0
- package/gateway/ai/backends/async_utils.py +21 -0
- package/gateway/ai/backends/deploy_bridge.py +150 -0
- package/gateway/ai/backends/gateway_core.py +261 -0
- package/gateway/ai/backends/generate_bridge.py +38 -0
- package/gateway/ai/backends/governance_bridge.py +196 -0
- package/gateway/ai/backends/intel_bridge.py +59 -0
- package/gateway/ai/backends/memory_bridge.py +93 -0
- package/gateway/ai/backends/ops_bridge.py +137 -0
- package/gateway/ai/backends/os_bridge.py +82 -0
- package/gateway/ai/backends/repo_bridge.py +117 -0
- package/gateway/ai/backends/ui_bridge.py +118 -0
- package/gateway/ai/backends/vault_bridge.py +129 -0
- package/gateway/ai/server.py +1182 -0
- package/gateway/core/__init__.py +3 -0
- package/gateway/core/__pycache__/__init__.cpython-310.pyc +0 -0
- package/gateway/core/__pycache__/auto_baseline.cpython-310.pyc +0 -0
- package/gateway/core/__pycache__/ci_formatter.cpython-310.pyc +0 -0
- package/gateway/core/__pycache__/contract_ledger.cpython-310.pyc +0 -0
- package/gateway/core/__pycache__/dependency_graph.cpython-310.pyc +0 -0
- package/gateway/core/__pycache__/dependency_manifest.cpython-310.pyc +0 -0
- package/gateway/core/__pycache__/diff_engine_v2.cpython-310.pyc +0 -0
- package/gateway/core/__pycache__/event_backbone.cpython-310.pyc +0 -0
- package/gateway/core/__pycache__/event_schema.cpython-310.pyc +0 -0
- package/gateway/core/__pycache__/explainer.cpython-310.pyc +0 -0
- package/gateway/core/__pycache__/gateway.cpython-310.pyc +0 -0
- package/gateway/core/__pycache__/gateway_v2.cpython-310.pyc +0 -0
- package/gateway/core/__pycache__/gateway_v3.cpython-310.pyc +0 -0
- package/gateway/core/__pycache__/impact_analyzer.cpython-310.pyc +0 -0
- package/gateway/core/__pycache__/policy_engine.cpython-310.pyc +0 -0
- package/gateway/core/__pycache__/registry.cpython-310.pyc +0 -0
- package/gateway/core/__pycache__/registry_v2.cpython-310.pyc +0 -0
- package/gateway/core/__pycache__/registry_v3.cpython-310.pyc +0 -0
- package/gateway/core/__pycache__/semver_classifier.cpython-310.pyc +0 -0
- package/gateway/core/__pycache__/spec_detector.cpython-310.pyc +0 -0
- package/gateway/core/__pycache__/surface_bridge.cpython-310.pyc +0 -0
- package/gateway/core/auto_baseline.py +304 -0
- package/gateway/core/ci_formatter.py +283 -0
- package/gateway/core/complexity_analyzer.py +386 -0
- package/gateway/core/contract_ledger.py +345 -0
- package/gateway/core/dependency_graph.py +218 -0
- package/gateway/core/dependency_manifest.py +223 -0
- package/gateway/core/diff_engine_v2.py +477 -0
- package/gateway/core/diff_engine_v2.py.bak +426 -0
- package/gateway/core/event_backbone.py +268 -0
- package/gateway/core/event_schema.py +258 -0
- package/gateway/core/explainer.py +438 -0
- package/gateway/core/gateway.py +128 -0
- package/gateway/core/gateway_v2.py +154 -0
- package/gateway/core/gateway_v3.py +224 -0
- package/gateway/core/impact_analyzer.py +163 -0
- package/gateway/core/policies/default.yml +13 -0
- package/gateway/core/policies/relaxed.yml +48 -0
- package/gateway/core/policies/strict.yml +55 -0
- package/gateway/core/policy_engine.py +464 -0
- package/gateway/core/registry.py +52 -0
- package/gateway/core/registry_v2.py +132 -0
- package/gateway/core/registry_v3.py +134 -0
- package/gateway/core/semver_classifier.py +152 -0
- package/gateway/core/spec_detector.py +130 -0
- package/gateway/core/surface_bridge.py +307 -0
- package/gateway/core/zero_spec/__init__.py +4 -0
- package/gateway/core/zero_spec/__pycache__/__init__.cpython-310.pyc +0 -0
- package/gateway/core/zero_spec/__pycache__/detector.cpython-310.pyc +0 -0
- package/gateway/core/zero_spec/__pycache__/express_extractor.cpython-310.pyc +0 -0
- package/gateway/core/zero_spec/__pycache__/fastapi_extractor.cpython-310.pyc +0 -0
- package/gateway/core/zero_spec/__pycache__/nestjs_extractor.cpython-310.pyc +0 -0
- package/gateway/core/zero_spec/detector.py +353 -0
- package/gateway/core/zero_spec/express_extractor.py +483 -0
- package/gateway/core/zero_spec/fastapi_extractor.py +254 -0
- package/gateway/core/zero_spec/nestjs_extractor.py +369 -0
- package/gateway/tasks/__init__.py +1 -0
- package/gateway/tasks/__pycache__/__init__.cpython-310.pyc +0 -0
- package/gateway/tasks/__pycache__/check_policy.cpython-310.pyc +0 -0
- package/gateway/tasks/__pycache__/check_policy_v2.cpython-310.pyc +0 -0
- package/gateway/tasks/__pycache__/check_policy_v3.cpython-310.pyc +0 -0
- package/gateway/tasks/__pycache__/explain_diff.cpython-310.pyc +0 -0
- package/gateway/tasks/__pycache__/explain_diff_v2.cpython-310.pyc +0 -0
- package/gateway/tasks/__pycache__/validate_api.cpython-310.pyc +0 -0
- package/gateway/tasks/__pycache__/validate_api_v2.cpython-310.pyc +0 -0
- package/gateway/tasks/__pycache__/validate_api_v3.cpython-310.pyc +0 -0
- package/gateway/tasks/check_policy.py +177 -0
- package/gateway/tasks/check_policy_v2.py +255 -0
- package/gateway/tasks/check_policy_v3.py +255 -0
- package/gateway/tasks/explain_diff.py +305 -0
- package/gateway/tasks/explain_diff_v2.py +267 -0
- package/gateway/tasks/validate_api.py +131 -0
- package/gateway/tasks/validate_api_v2.py +208 -0
- package/gateway/tasks/validate_api_v3.py +163 -0
- package/package.json +3 -3
- package/adapters/codex-skill.js +0 -87
- package/adapters/cursor-extension.js +0 -190
- package/adapters/gemini-action.js +0 -93
- package/adapters/openai-function.js +0 -112
- package/adapters/xai-plugin.js +0 -151
- package/test-decision-engine.js +0 -181
- package/test-hook.js +0 -27
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Delimit™ Codex Jamsons Adapter
|
|
4
|
+
* Layer: Jamsons OS (strategy + operational governance)
|
|
5
|
+
* Injects portfolio context, logs decisions, surfaces venture/priority state.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
'use strict';
|
|
9
|
+
|
|
10
|
+
const axios = require('axios');
|
|
11
|
+
const AGENT_URL = `http://127.0.0.1:${process.env.DELIMIT_AGENT_PORT || 7823}`;
|
|
12
|
+
const JAMSONS_URL = `http://localhost:${process.env.JAMSONS_PORT || 8091}`;
|
|
13
|
+
|
|
14
|
+
const VENTURES = {
|
|
15
|
+
delimit: { priority: 'P0', status: 'active' },
|
|
16
|
+
domainvested: { priority: 'P2', status: 'maintenance' },
|
|
17
|
+
'wire.report': { priority: 'held', status: 'held' },
|
|
18
|
+
'livetube.ai': { priority: 'held', status: 'held' },
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
class DelimitCodexJamsons {
|
|
22
|
+
constructor() {
|
|
23
|
+
this.name = 'delimit-jamsons';
|
|
24
|
+
this.version = '1.0.0';
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Before suggestion: inject portfolio context so the model has strategic awareness.
|
|
29
|
+
*/
|
|
30
|
+
async onBeforeSuggestion(context) {
|
|
31
|
+
try {
|
|
32
|
+
const portfolioCtx = await this._getPortfolioContext(context);
|
|
33
|
+
// Attach context metadata — Codex passes this through to the model
|
|
34
|
+
context._jamsons = portfolioCtx;
|
|
35
|
+
} catch (_) { /* fail open */ }
|
|
36
|
+
return { allow: true };
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
async onAfterAccept(context) {
|
|
40
|
+
// Log accepted suggestion to Jamsons decision ledger
|
|
41
|
+
try {
|
|
42
|
+
await axios.post(`${JAMSONS_URL}/api/chatops/decisions`, {
|
|
43
|
+
type: 'task',
|
|
44
|
+
title: `Codex: accepted suggestion in ${context.file || 'unknown file'}`,
|
|
45
|
+
detail: `Language: ${context.language || 'unknown'} | Tool: codex`,
|
|
46
|
+
user_id: 'codex-adapter',
|
|
47
|
+
tags: ['delimit', 'chatops'],
|
|
48
|
+
}, {
|
|
49
|
+
headers: { Authorization: `Bearer ${process.env.CHATOPS_AUTH_TOKEN}` },
|
|
50
|
+
timeout: 2000,
|
|
51
|
+
});
|
|
52
|
+
} catch (_) { /* silent */ }
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
async _getPortfolioContext(context) {
|
|
56
|
+
const [memory, decisions] = await Promise.allSettled([
|
|
57
|
+
this._searchMemory(context.file || context.language || 'delimit'),
|
|
58
|
+
this._getPendingDecisions(),
|
|
59
|
+
]);
|
|
60
|
+
|
|
61
|
+
return {
|
|
62
|
+
ventures: VENTURES,
|
|
63
|
+
activeVenture: this._detectVenture(context),
|
|
64
|
+
memory: memory.status === 'fulfilled' ? memory.value : [],
|
|
65
|
+
pendingDecisions: decisions.status === 'fulfilled' ? decisions.value : [],
|
|
66
|
+
timestamp: new Date().toISOString(),
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
_detectVenture(context) {
|
|
71
|
+
const path = (context.file || '').toLowerCase();
|
|
72
|
+
if (path.includes('delimit')) return 'delimit';
|
|
73
|
+
if (path.includes('domainvested')) return 'domainvested';
|
|
74
|
+
return 'delimit'; // default active venture
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
async _searchMemory(query) {
|
|
78
|
+
const r = await axios.get(`${JAMSONS_URL}/api/memory/search`, {
|
|
79
|
+
params: { q: query, limit: 5 },
|
|
80
|
+
timeout: 2000,
|
|
81
|
+
});
|
|
82
|
+
return r.data?.results || [];
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
async _getPendingDecisions() {
|
|
86
|
+
const r = await axios.get(`${JAMSONS_URL}/api/chatops/decisions`, {
|
|
87
|
+
params: { status: 'pending', limit: 5 },
|
|
88
|
+
headers: { Authorization: `Bearer ${process.env.CHATOPS_AUTH_TOKEN}` },
|
|
89
|
+
timeout: 2000,
|
|
90
|
+
});
|
|
91
|
+
return r.data?.decisions || r.data || [];
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
async handleCommand(command, _args) {
|
|
95
|
+
const { execSync } = require('child_process');
|
|
96
|
+
const cmds = {
|
|
97
|
+
'jamsons': 'delimit status --layer=jamsons',
|
|
98
|
+
'portfolio': 'delimit portfolio --summary',
|
|
99
|
+
'decisions': 'delimit decisions --pending',
|
|
100
|
+
'memory': 'delimit memory --recent',
|
|
101
|
+
};
|
|
102
|
+
if (cmds[command]) {
|
|
103
|
+
try {
|
|
104
|
+
return execSync(cmds[command], { timeout: 10000 }).toString();
|
|
105
|
+
} catch (e) {
|
|
106
|
+
return `[JAMSONS] Command failed: ${e.message}`;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Returns structured Jamsons context for consensus use.
|
|
113
|
+
*/
|
|
114
|
+
async getContext() {
|
|
115
|
+
const [memory, decisions] = await Promise.allSettled([
|
|
116
|
+
axios.get(`${JAMSONS_URL}/api/memory/search`, {
|
|
117
|
+
params: { q: 'delimit strategy', limit: 5 },
|
|
118
|
+
timeout: 3000,
|
|
119
|
+
}),
|
|
120
|
+
axios.get(`${JAMSONS_URL}/api/chatops/decisions`, {
|
|
121
|
+
params: { status: 'pending' },
|
|
122
|
+
headers: { Authorization: `Bearer ${process.env.CHATOPS_AUTH_TOKEN}` },
|
|
123
|
+
timeout: 3000,
|
|
124
|
+
}),
|
|
125
|
+
]);
|
|
126
|
+
|
|
127
|
+
return {
|
|
128
|
+
layer: 'jamsons',
|
|
129
|
+
ventures: VENTURES,
|
|
130
|
+
memory: memory.status === 'fulfilled' ? (memory.value.data?.results || []) : null,
|
|
131
|
+
pendingDecisions: decisions.status === 'fulfilled' ? (decisions.value.data || []) : null,
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
if (typeof module !== 'undefined' && module.exports) {
|
|
137
|
+
module.exports = new DelimitCodexJamsons();
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
if (typeof registerSkill === 'function') {
|
|
141
|
+
registerSkill(new DelimitCodexJamsons());
|
|
142
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Delimit™ Codex Security Skill Adapter
|
|
4
|
+
* Layer: Delimit (code governance) — Security surface
|
|
5
|
+
* Triggered on pre-code-generation and pre-suggestion events
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
'use strict';
|
|
9
|
+
|
|
10
|
+
const axios = require('axios');
|
|
11
|
+
const AGENT_URL = `http://127.0.0.1:${process.env.DELIMIT_AGENT_PORT || 7823}`;
|
|
12
|
+
|
|
13
|
+
class DelimitCodexSecurity {
|
|
14
|
+
constructor() {
|
|
15
|
+
this.name = 'delimit-security';
|
|
16
|
+
this.version = '1.0.0';
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
async onBeforeSuggestion(context) {
|
|
20
|
+
try {
|
|
21
|
+
const { code, language, file } = context;
|
|
22
|
+
const response = await axios.post(`${AGENT_URL}/security`, {
|
|
23
|
+
action: 'codex_suggestion',
|
|
24
|
+
code,
|
|
25
|
+
language,
|
|
26
|
+
file,
|
|
27
|
+
tool: 'codex',
|
|
28
|
+
}, { timeout: 3000 });
|
|
29
|
+
|
|
30
|
+
const { severity, findings } = response.data;
|
|
31
|
+
|
|
32
|
+
if (severity === 'critical') {
|
|
33
|
+
return {
|
|
34
|
+
allow: false,
|
|
35
|
+
message: `[DELIMIT SECURITY] Blocked — critical finding: ${findings?.[0]?.message || 'see audit log'}`,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (severity === 'high') {
|
|
40
|
+
return {
|
|
41
|
+
allow: true,
|
|
42
|
+
warning: `[DELIMIT SECURITY] High-severity finding: ${findings?.[0]?.message || 'review before accepting'}`,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return { allow: true };
|
|
47
|
+
} catch (err) {
|
|
48
|
+
if (err.response?.data?.action === 'block') {
|
|
49
|
+
return { allow: false, message: `[DELIMIT SECURITY] ${err.response.data.reason}` };
|
|
50
|
+
}
|
|
51
|
+
// Fail open — security service unavailable
|
|
52
|
+
console.warn('[DELIMIT SECURITY] Scan unavailable:', err.message);
|
|
53
|
+
return { allow: true };
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
async onAfterAccept(context) {
|
|
58
|
+
try {
|
|
59
|
+
await axios.post(`${AGENT_URL}/audit`, {
|
|
60
|
+
action: 'codex_security_accept',
|
|
61
|
+
context,
|
|
62
|
+
timestamp: new Date().toISOString(),
|
|
63
|
+
}, { timeout: 2000 });
|
|
64
|
+
} catch (_) { /* silent */ }
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
async handleCommand(command, _args) {
|
|
68
|
+
if (command === 'security') {
|
|
69
|
+
const { execSync } = require('child_process');
|
|
70
|
+
try {
|
|
71
|
+
return execSync('delimit security --verbose', { timeout: 10000 }).toString();
|
|
72
|
+
} catch (e) {
|
|
73
|
+
return `[DELIMIT SECURITY] Command failed: ${e.message}`;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
async getContext() {
|
|
79
|
+
try {
|
|
80
|
+
const r = await axios.get(`${AGENT_URL}/security/status`, { timeout: 3000 });
|
|
81
|
+
return { layer: 'delimit-security', status: 'ok', data: r.data };
|
|
82
|
+
} catch (_) {
|
|
83
|
+
return { layer: 'delimit-security', status: 'unavailable' };
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (typeof module !== 'undefined' && module.exports) {
|
|
89
|
+
module.exports = new DelimitCodexSecurity();
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (typeof registerSkill === 'function') {
|
|
93
|
+
registerSkill(new DelimitCodexSecurity());
|
|
94
|
+
}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Delimit™ Gemini CLI Forge Adapter
|
|
4
|
+
* Layer: Forge (execution governance)
|
|
5
|
+
* Used as: command handler called from Gemini CLI hooks and @commands
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
'use strict';
|
|
9
|
+
|
|
10
|
+
const axios = require('axios');
|
|
11
|
+
const AGENT_URL = `http://127.0.0.1:${process.env.DELIMIT_AGENT_PORT || 7823}`;
|
|
12
|
+
|
|
13
|
+
class DelimitGeminiForge {
|
|
14
|
+
constructor() {
|
|
15
|
+
this.id = 'delimit-forge';
|
|
16
|
+
this.version = '1.0.0';
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Pre-generation: surface test failures and deploy locks to Gemini before it generates code.
|
|
21
|
+
*/
|
|
22
|
+
async beforeCodeGeneration(request) {
|
|
23
|
+
const [testState, deployState, releaseState] = await Promise.allSettled([
|
|
24
|
+
axios.get(`${AGENT_URL}/test/status`, { timeout: 3000 }),
|
|
25
|
+
axios.get(`${AGENT_URL}/deploy/status`, { timeout: 3000 }),
|
|
26
|
+
axios.get(`${AGENT_URL}/release/status`, { timeout: 3000 }),
|
|
27
|
+
]);
|
|
28
|
+
|
|
29
|
+
const warnings = [];
|
|
30
|
+
|
|
31
|
+
if (testState.status === 'fulfilled') {
|
|
32
|
+
const t = testState.value.data;
|
|
33
|
+
if (t?.failing > 0) warnings.push(`[FORGE] ${t.failing} test(s) failing`);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (deployState.status === 'fulfilled') {
|
|
37
|
+
const d = deployState.value.data;
|
|
38
|
+
if (d?.locked) warnings.push(`[FORGE] Deploy locked: ${d.reason || 'release in progress'}`);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (releaseState.status === 'fulfilled') {
|
|
42
|
+
const rel = releaseState.value.data;
|
|
43
|
+
if (rel?.in_progress) warnings.push(`[FORGE] Release in progress: ${rel.version || 'unknown'}`);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (warnings.length > 0) {
|
|
47
|
+
console.warn(warnings.join('\n'));
|
|
48
|
+
// Inject warnings into system prompt context
|
|
49
|
+
request._forgeWarnings = warnings;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return request;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
async afterResponse(response) {
|
|
56
|
+
try {
|
|
57
|
+
await axios.post(`${AGENT_URL}/audit`, {
|
|
58
|
+
action: 'gemini_forge_response',
|
|
59
|
+
response: {
|
|
60
|
+
model: response.model,
|
|
61
|
+
tokens: response.usage,
|
|
62
|
+
timestamp: new Date().toISOString(),
|
|
63
|
+
},
|
|
64
|
+
}, { timeout: 2000 });
|
|
65
|
+
} catch (_) { /* silent */ }
|
|
66
|
+
return response;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
async handleCommand(command, _args) {
|
|
70
|
+
const { execSync } = require('child_process');
|
|
71
|
+
const commands = {
|
|
72
|
+
'@forge': 'delimit status --layer=forge',
|
|
73
|
+
'@tests': 'delimit test --summary',
|
|
74
|
+
'@deploy': 'delimit deploy --status',
|
|
75
|
+
'@release': 'delimit release --status',
|
|
76
|
+
};
|
|
77
|
+
if (commands[command]) {
|
|
78
|
+
try {
|
|
79
|
+
return execSync(commands[command], { timeout: 10000 }).toString();
|
|
80
|
+
} catch (e) {
|
|
81
|
+
return `[FORGE] Command failed: ${e.message}`;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Structured context for consensus — call this from hooks or consensus runners.
|
|
88
|
+
*/
|
|
89
|
+
async getContext() {
|
|
90
|
+
const [tests, deploy, release] = await Promise.allSettled([
|
|
91
|
+
axios.get(`${AGENT_URL}/test/status`, { timeout: 3000 }),
|
|
92
|
+
axios.get(`${AGENT_URL}/deploy/status`, { timeout: 3000 }),
|
|
93
|
+
axios.get(`${AGENT_URL}/release/status`, { timeout: 3000 }),
|
|
94
|
+
]);
|
|
95
|
+
return {
|
|
96
|
+
layer: 'forge',
|
|
97
|
+
tool: 'gemini',
|
|
98
|
+
tests: tests.status === 'fulfilled' ? tests.value.data : null,
|
|
99
|
+
deploy: deploy.status === 'fulfilled' ? deploy.value.data : null,
|
|
100
|
+
release: release.status === 'fulfilled' ? release.value.data : null,
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
module.exports = new DelimitGeminiForge();
|
|
106
|
+
|
|
107
|
+
if (typeof registerExtension === 'function') {
|
|
108
|
+
registerExtension(new DelimitGeminiForge());
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// CLI entrypoint for Gemini hook invocation
|
|
112
|
+
if (require.main === module) {
|
|
113
|
+
const instance = new DelimitGeminiForge();
|
|
114
|
+
instance.getContext()
|
|
115
|
+
.then(ctx => {
|
|
116
|
+
process.stdout.write(JSON.stringify(ctx) + '\n');
|
|
117
|
+
process.exit(0);
|
|
118
|
+
})
|
|
119
|
+
.catch(() => process.exit(0)); // fail open
|
|
120
|
+
}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Delimit™ Gemini CLI Jamsons Adapter
|
|
4
|
+
* Layer: Jamsons OS (strategy + operational governance)
|
|
5
|
+
* Injects portfolio context, logs decisions, surfaces venture/priority state.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
'use strict';
|
|
9
|
+
|
|
10
|
+
const axios = require('axios');
|
|
11
|
+
const AGENT_URL = `http://127.0.0.1:${process.env.DELIMIT_AGENT_PORT || 7823}`;
|
|
12
|
+
const JAMSONS_URL = `http://localhost:${process.env.JAMSONS_PORT || 8091}`;
|
|
13
|
+
|
|
14
|
+
const VENTURES = {
|
|
15
|
+
delimit: { priority: 'P0', status: 'active' },
|
|
16
|
+
domainvested: { priority: 'P2', status: 'maintenance' },
|
|
17
|
+
'wire.report': { priority: 'held', status: 'held' },
|
|
18
|
+
'livetube.ai': { priority: 'held', status: 'held' },
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
class DelimitGeminiJamsons {
|
|
22
|
+
constructor() {
|
|
23
|
+
this.id = 'delimit-jamsons';
|
|
24
|
+
this.version = '1.0.0';
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Pre-generation: inject portfolio and decision context into request.
|
|
29
|
+
*/
|
|
30
|
+
async beforeCodeGeneration(request) {
|
|
31
|
+
try {
|
|
32
|
+
const portfolioCtx = await this._getPortfolioContext(request);
|
|
33
|
+
request._jamsons = portfolioCtx;
|
|
34
|
+
} catch (_) { /* fail open */ }
|
|
35
|
+
return request;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async afterResponse(response) {
|
|
39
|
+
try {
|
|
40
|
+
await axios.post(`${JAMSONS_URL}/api/chatops/decisions`, {
|
|
41
|
+
type: 'task',
|
|
42
|
+
title: `Gemini: session response logged`,
|
|
43
|
+
detail: `Model: ${response.model || 'unknown'} | Tool: gemini`,
|
|
44
|
+
user_id: 'gemini-adapter',
|
|
45
|
+
tags: ['delimit', 'chatops'],
|
|
46
|
+
}, {
|
|
47
|
+
headers: { Authorization: `Bearer ${process.env.CHATOPS_AUTH_TOKEN}` },
|
|
48
|
+
timeout: 2000,
|
|
49
|
+
});
|
|
50
|
+
} catch (_) { /* silent */ }
|
|
51
|
+
return response;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
async _getPortfolioContext(request) {
|
|
55
|
+
const [memory, decisions] = await Promise.allSettled([
|
|
56
|
+
this._searchMemory(request.prompt || 'delimit'),
|
|
57
|
+
this._getPendingDecisions(),
|
|
58
|
+
]);
|
|
59
|
+
|
|
60
|
+
return {
|
|
61
|
+
ventures: VENTURES,
|
|
62
|
+
activeVenture: this._detectVenture(request),
|
|
63
|
+
memory: memory.status === 'fulfilled' ? memory.value : [],
|
|
64
|
+
pendingDecisions: decisions.status === 'fulfilled' ? decisions.value : [],
|
|
65
|
+
timestamp: new Date().toISOString(),
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
_detectVenture(request) {
|
|
70
|
+
const text = (request.prompt || request.context || '').toLowerCase();
|
|
71
|
+
if (text.includes('delimit')) return 'delimit';
|
|
72
|
+
if (text.includes('domainvested')) return 'domainvested';
|
|
73
|
+
return 'delimit';
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
async _searchMemory(query) {
|
|
77
|
+
const r = await axios.get(`${JAMSONS_URL}/api/memory/search`, {
|
|
78
|
+
params: { q: query, limit: 5 },
|
|
79
|
+
timeout: 2000,
|
|
80
|
+
});
|
|
81
|
+
return r.data?.results || [];
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
async _getPendingDecisions() {
|
|
85
|
+
const r = await axios.get(`${JAMSONS_URL}/api/chatops/decisions`, {
|
|
86
|
+
params: { status: 'pending', limit: 5 },
|
|
87
|
+
headers: { Authorization: `Bearer ${process.env.CHATOPS_AUTH_TOKEN}` },
|
|
88
|
+
timeout: 2000,
|
|
89
|
+
});
|
|
90
|
+
return r.data?.decisions || r.data || [];
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
async handleCommand(command, _args) {
|
|
94
|
+
const { execSync } = require('child_process');
|
|
95
|
+
const commands = {
|
|
96
|
+
'@jamsons': 'delimit status --layer=jamsons',
|
|
97
|
+
'@portfolio': 'delimit portfolio --summary',
|
|
98
|
+
'@decisions': 'delimit decisions --pending',
|
|
99
|
+
'@memory': 'delimit memory --recent',
|
|
100
|
+
};
|
|
101
|
+
if (commands[command]) {
|
|
102
|
+
try {
|
|
103
|
+
return execSync(commands[command], { timeout: 10000 }).toString();
|
|
104
|
+
} catch (e) {
|
|
105
|
+
return `[JAMSONS] Command failed: ${e.message}`;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Structured context for consensus — call from hooks or consensus runners.
|
|
112
|
+
*/
|
|
113
|
+
async getContext() {
|
|
114
|
+
const [memory, decisions] = await Promise.allSettled([
|
|
115
|
+
axios.get(`${JAMSONS_URL}/api/memory/search`, {
|
|
116
|
+
params: { q: 'delimit strategy', limit: 5 },
|
|
117
|
+
timeout: 3000,
|
|
118
|
+
}),
|
|
119
|
+
axios.get(`${JAMSONS_URL}/api/chatops/decisions`, {
|
|
120
|
+
params: { status: 'pending' },
|
|
121
|
+
headers: { Authorization: `Bearer ${process.env.CHATOPS_AUTH_TOKEN}` },
|
|
122
|
+
timeout: 3000,
|
|
123
|
+
}),
|
|
124
|
+
]);
|
|
125
|
+
|
|
126
|
+
return {
|
|
127
|
+
layer: 'jamsons',
|
|
128
|
+
tool: 'gemini',
|
|
129
|
+
ventures: VENTURES,
|
|
130
|
+
memory: memory.status === 'fulfilled' ? (memory.value.data?.results || []) : null,
|
|
131
|
+
pendingDecisions: decisions.status === 'fulfilled' ? (decisions.value.data || []) : null,
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
module.exports = new DelimitGeminiJamsons();
|
|
137
|
+
|
|
138
|
+
if (typeof registerExtension === 'function') {
|
|
139
|
+
registerExtension(new DelimitGeminiJamsons());
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// CLI entrypoint for Gemini hook invocation
|
|
143
|
+
if (require.main === module) {
|
|
144
|
+
const instance = new DelimitGeminiJamsons();
|
|
145
|
+
const event = process.argv[2] || 'before-agent';
|
|
146
|
+
instance.getContext()
|
|
147
|
+
.then(ctx => {
|
|
148
|
+
process.stdout.write(JSON.stringify(ctx) + '\n');
|
|
149
|
+
process.exit(0);
|
|
150
|
+
})
|
|
151
|
+
.catch(() => process.exit(0)); // fail open
|
|
152
|
+
}
|
package/bin/delimit-cli.js
CHANGED
|
@@ -50,7 +50,7 @@ async function ensureAgent() {
|
|
|
50
50
|
program
|
|
51
51
|
.name('delimit')
|
|
52
52
|
.description('Prevent breaking API changes before they reach production')
|
|
53
|
-
.version('
|
|
53
|
+
.version(require('../package.json').version);
|
|
54
54
|
|
|
55
55
|
// Install command with modes
|
|
56
56
|
program
|
|
@@ -803,7 +803,7 @@ program
|
|
|
803
803
|
const specPath = foundSpecs[0];
|
|
804
804
|
console.log(` Detected spec: ${chalk.bold(specPath)}`);
|
|
805
805
|
console.log('');
|
|
806
|
-
console.log(chalk.bold('
|
|
806
|
+
console.log(chalk.bold(' Workflow template:\n'));
|
|
807
807
|
console.log(chalk.gray(` name: API Governance
|
|
808
808
|
on:
|
|
809
809
|
pull_request:
|
|
@@ -827,6 +827,48 @@ program
|
|
|
827
827
|
new_spec: ${specPath}
|
|
828
828
|
mode: advisory`));
|
|
829
829
|
console.log('');
|
|
830
|
+
|
|
831
|
+
// Auto-write the workflow file
|
|
832
|
+
const workflowDir = path.join(process.cwd(), '.github', 'workflows');
|
|
833
|
+
const workflowFile = path.join(workflowDir, 'api-governance.yml');
|
|
834
|
+
|
|
835
|
+
if (!fs.existsSync(workflowFile)) {
|
|
836
|
+
try {
|
|
837
|
+
fs.mkdirSync(workflowDir, { recursive: true });
|
|
838
|
+
const workflowContent = `name: API Governance
|
|
839
|
+
on:
|
|
840
|
+
pull_request:
|
|
841
|
+
paths:
|
|
842
|
+
- '${specPath}'
|
|
843
|
+
|
|
844
|
+
permissions:
|
|
845
|
+
contents: read
|
|
846
|
+
pull-requests: write
|
|
847
|
+
|
|
848
|
+
jobs:
|
|
849
|
+
api-governance:
|
|
850
|
+
runs-on: ubuntu-latest
|
|
851
|
+
steps:
|
|
852
|
+
- uses: actions/checkout@v4
|
|
853
|
+
- uses: actions/checkout@v4
|
|
854
|
+
with:
|
|
855
|
+
ref: \${{ github.event.pull_request.base.sha }}
|
|
856
|
+
path: _base
|
|
857
|
+
- uses: delimit-ai/delimit@v1
|
|
858
|
+
with:
|
|
859
|
+
old_spec: _base/${specPath}
|
|
860
|
+
new_spec: ${specPath}
|
|
861
|
+
mode: advisory
|
|
862
|
+
`;
|
|
863
|
+
fs.writeFileSync(workflowFile, workflowContent);
|
|
864
|
+
console.log(chalk.green(` Created .github/workflows/api-governance.yml\n`));
|
|
865
|
+
} catch (err) {
|
|
866
|
+
console.log(chalk.yellow(` Could not write workflow file: ${err.message}`));
|
|
867
|
+
console.log(chalk.bold(' Add this to .github/workflows/api-governance.yml manually (shown above)\n'));
|
|
868
|
+
}
|
|
869
|
+
} else {
|
|
870
|
+
console.log(chalk.yellow(' .github/workflows/api-governance.yml already exists — skipped\n'));
|
|
871
|
+
}
|
|
830
872
|
} else {
|
|
831
873
|
console.log(' No OpenAPI spec file detected.');
|
|
832
874
|
console.log(` Delimit also supports ${chalk.bold('Zero-Spec Mode')} — run ${chalk.bold('delimit lint')} in a FastAPI/NestJS/Express project.`);
|
|
@@ -1144,6 +1186,14 @@ program
|
|
|
1144
1186
|
}
|
|
1145
1187
|
});
|
|
1146
1188
|
|
|
1189
|
+
// Setup command — install MCP governance tools into Claude Code
|
|
1190
|
+
program
|
|
1191
|
+
.command('setup')
|
|
1192
|
+
.description('Install Delimit MCP governance tools into Claude Code')
|
|
1193
|
+
.action(() => {
|
|
1194
|
+
require('./delimit-setup.js');
|
|
1195
|
+
});
|
|
1196
|
+
|
|
1147
1197
|
// Hide legacy/internal commands from --help
|
|
1148
1198
|
['install', 'mode', 'status', 'policy', 'auth', 'audit',
|
|
1149
1199
|
'explain-decision', 'uninstall', 'proxy', 'hook'].forEach(name => {
|