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.
Files changed (113) hide show
  1. package/.dockerignore +7 -0
  2. package/.github/workflows/ci.yml +22 -0
  3. package/CHANGELOG.md +33 -0
  4. package/CODE_OF_CONDUCT.md +48 -0
  5. package/CONTRIBUTING.md +67 -0
  6. package/Dockerfile +9 -0
  7. package/LICENSE +21 -0
  8. package/README.md +51 -130
  9. package/SECURITY.md +42 -0
  10. package/adapters/codex-forge.js +107 -0
  11. package/adapters/codex-jamsons.js +142 -0
  12. package/adapters/codex-security.js +94 -0
  13. package/adapters/gemini-forge.js +120 -0
  14. package/adapters/gemini-jamsons.js +152 -0
  15. package/bin/delimit-cli.js +52 -2
  16. package/bin/delimit-setup.js +258 -0
  17. package/gateway/ai/backends/__init__.py +0 -0
  18. package/gateway/ai/backends/async_utils.py +21 -0
  19. package/gateway/ai/backends/deploy_bridge.py +150 -0
  20. package/gateway/ai/backends/gateway_core.py +261 -0
  21. package/gateway/ai/backends/generate_bridge.py +38 -0
  22. package/gateway/ai/backends/governance_bridge.py +196 -0
  23. package/gateway/ai/backends/intel_bridge.py +59 -0
  24. package/gateway/ai/backends/memory_bridge.py +93 -0
  25. package/gateway/ai/backends/ops_bridge.py +137 -0
  26. package/gateway/ai/backends/os_bridge.py +82 -0
  27. package/gateway/ai/backends/repo_bridge.py +117 -0
  28. package/gateway/ai/backends/ui_bridge.py +118 -0
  29. package/gateway/ai/backends/vault_bridge.py +129 -0
  30. package/gateway/ai/server.py +1182 -0
  31. package/gateway/core/__init__.py +3 -0
  32. package/gateway/core/__pycache__/__init__.cpython-310.pyc +0 -0
  33. package/gateway/core/__pycache__/auto_baseline.cpython-310.pyc +0 -0
  34. package/gateway/core/__pycache__/ci_formatter.cpython-310.pyc +0 -0
  35. package/gateway/core/__pycache__/contract_ledger.cpython-310.pyc +0 -0
  36. package/gateway/core/__pycache__/dependency_graph.cpython-310.pyc +0 -0
  37. package/gateway/core/__pycache__/dependency_manifest.cpython-310.pyc +0 -0
  38. package/gateway/core/__pycache__/diff_engine_v2.cpython-310.pyc +0 -0
  39. package/gateway/core/__pycache__/event_backbone.cpython-310.pyc +0 -0
  40. package/gateway/core/__pycache__/event_schema.cpython-310.pyc +0 -0
  41. package/gateway/core/__pycache__/explainer.cpython-310.pyc +0 -0
  42. package/gateway/core/__pycache__/gateway.cpython-310.pyc +0 -0
  43. package/gateway/core/__pycache__/gateway_v2.cpython-310.pyc +0 -0
  44. package/gateway/core/__pycache__/gateway_v3.cpython-310.pyc +0 -0
  45. package/gateway/core/__pycache__/impact_analyzer.cpython-310.pyc +0 -0
  46. package/gateway/core/__pycache__/policy_engine.cpython-310.pyc +0 -0
  47. package/gateway/core/__pycache__/registry.cpython-310.pyc +0 -0
  48. package/gateway/core/__pycache__/registry_v2.cpython-310.pyc +0 -0
  49. package/gateway/core/__pycache__/registry_v3.cpython-310.pyc +0 -0
  50. package/gateway/core/__pycache__/semver_classifier.cpython-310.pyc +0 -0
  51. package/gateway/core/__pycache__/spec_detector.cpython-310.pyc +0 -0
  52. package/gateway/core/__pycache__/surface_bridge.cpython-310.pyc +0 -0
  53. package/gateway/core/auto_baseline.py +304 -0
  54. package/gateway/core/ci_formatter.py +283 -0
  55. package/gateway/core/complexity_analyzer.py +386 -0
  56. package/gateway/core/contract_ledger.py +345 -0
  57. package/gateway/core/dependency_graph.py +218 -0
  58. package/gateway/core/dependency_manifest.py +223 -0
  59. package/gateway/core/diff_engine_v2.py +477 -0
  60. package/gateway/core/diff_engine_v2.py.bak +426 -0
  61. package/gateway/core/event_backbone.py +268 -0
  62. package/gateway/core/event_schema.py +258 -0
  63. package/gateway/core/explainer.py +438 -0
  64. package/gateway/core/gateway.py +128 -0
  65. package/gateway/core/gateway_v2.py +154 -0
  66. package/gateway/core/gateway_v3.py +224 -0
  67. package/gateway/core/impact_analyzer.py +163 -0
  68. package/gateway/core/policies/default.yml +13 -0
  69. package/gateway/core/policies/relaxed.yml +48 -0
  70. package/gateway/core/policies/strict.yml +55 -0
  71. package/gateway/core/policy_engine.py +464 -0
  72. package/gateway/core/registry.py +52 -0
  73. package/gateway/core/registry_v2.py +132 -0
  74. package/gateway/core/registry_v3.py +134 -0
  75. package/gateway/core/semver_classifier.py +152 -0
  76. package/gateway/core/spec_detector.py +130 -0
  77. package/gateway/core/surface_bridge.py +307 -0
  78. package/gateway/core/zero_spec/__init__.py +4 -0
  79. package/gateway/core/zero_spec/__pycache__/__init__.cpython-310.pyc +0 -0
  80. package/gateway/core/zero_spec/__pycache__/detector.cpython-310.pyc +0 -0
  81. package/gateway/core/zero_spec/__pycache__/express_extractor.cpython-310.pyc +0 -0
  82. package/gateway/core/zero_spec/__pycache__/fastapi_extractor.cpython-310.pyc +0 -0
  83. package/gateway/core/zero_spec/__pycache__/nestjs_extractor.cpython-310.pyc +0 -0
  84. package/gateway/core/zero_spec/detector.py +353 -0
  85. package/gateway/core/zero_spec/express_extractor.py +483 -0
  86. package/gateway/core/zero_spec/fastapi_extractor.py +254 -0
  87. package/gateway/core/zero_spec/nestjs_extractor.py +369 -0
  88. package/gateway/tasks/__init__.py +1 -0
  89. package/gateway/tasks/__pycache__/__init__.cpython-310.pyc +0 -0
  90. package/gateway/tasks/__pycache__/check_policy.cpython-310.pyc +0 -0
  91. package/gateway/tasks/__pycache__/check_policy_v2.cpython-310.pyc +0 -0
  92. package/gateway/tasks/__pycache__/check_policy_v3.cpython-310.pyc +0 -0
  93. package/gateway/tasks/__pycache__/explain_diff.cpython-310.pyc +0 -0
  94. package/gateway/tasks/__pycache__/explain_diff_v2.cpython-310.pyc +0 -0
  95. package/gateway/tasks/__pycache__/validate_api.cpython-310.pyc +0 -0
  96. package/gateway/tasks/__pycache__/validate_api_v2.cpython-310.pyc +0 -0
  97. package/gateway/tasks/__pycache__/validate_api_v3.cpython-310.pyc +0 -0
  98. package/gateway/tasks/check_policy.py +177 -0
  99. package/gateway/tasks/check_policy_v2.py +255 -0
  100. package/gateway/tasks/check_policy_v3.py +255 -0
  101. package/gateway/tasks/explain_diff.py +305 -0
  102. package/gateway/tasks/explain_diff_v2.py +267 -0
  103. package/gateway/tasks/validate_api.py +131 -0
  104. package/gateway/tasks/validate_api_v2.py +208 -0
  105. package/gateway/tasks/validate_api_v3.py +163 -0
  106. package/package.json +3 -3
  107. package/adapters/codex-skill.js +0 -87
  108. package/adapters/cursor-extension.js +0 -190
  109. package/adapters/gemini-action.js +0 -93
  110. package/adapters/openai-function.js +0 -112
  111. package/adapters/xai-plugin.js +0 -151
  112. package/test-decision-engine.js +0 -181
  113. 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
+ }
@@ -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('2.3.0');
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(' Add this to .github/workflows/api-governance.yml:\n'));
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 => {