karajan-code 1.59.1 → 2.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 (112) hide show
  1. package/README.md +2 -0
  2. package/package.json +1 -1
  3. package/scripts/install.js +18 -15
  4. package/src/agents/aider-agent.js +0 -7
  5. package/src/agents/claude-agent.js +1 -4
  6. package/src/agents/codex-agent.js +2 -6
  7. package/src/agents/gemini-agent.js +0 -4
  8. package/src/agents/opencode-agent.js +0 -4
  9. package/src/{becaria → ci}/dispatch.js +13 -13
  10. package/src/{becaria → ci}/pr-diff.js +1 -1
  11. package/src/cli.js +2 -2
  12. package/src/commands/agents.js +0 -1
  13. package/src/commands/doctor.js +22 -40
  14. package/src/commands/init.js +8 -8
  15. package/src/commands/report.js +0 -13
  16. package/src/commands/review.js +17 -17
  17. package/src/commands/run.js +1 -2
  18. package/src/commands/status.js +1 -7
  19. package/src/config.js +10 -52
  20. package/src/git/automation.js +9 -9
  21. package/src/guards/output-guard.js +0 -17
  22. package/src/mcp/handlers/direct-handlers.js +1 -1
  23. package/src/mcp/handlers/run-handler.js +1 -2
  24. package/src/mcp/sovereignty-guard.js +1 -1
  25. package/src/mcp/tools.js +1 -1
  26. package/src/orchestrator/brain-coordinator.js +165 -0
  27. package/src/orchestrator/ci-integration.js +139 -0
  28. package/src/orchestrator/config-init.js +435 -0
  29. package/src/orchestrator/direct-actions.js +186 -0
  30. package/src/orchestrator/feedback-enrichment.js +140 -0
  31. package/src/orchestrator/feedback-queue.js +152 -0
  32. package/src/orchestrator/flow-control.js +159 -0
  33. package/src/orchestrator/post-loop-stages.js +20 -15
  34. package/src/orchestrator/preflight-checks.js +7 -3
  35. package/src/orchestrator/role-output-compressor.js +190 -0
  36. package/src/orchestrator/session-journal.js +344 -0
  37. package/src/orchestrator/solomon-escalation.js +38 -5
  38. package/src/orchestrator/solomon-rules.js +32 -33
  39. package/src/orchestrator/stages/reviewer-stage.js +16 -5
  40. package/src/orchestrator/stages/sonar-stage.js +24 -1
  41. package/src/orchestrator/verification-gate.js +146 -0
  42. package/src/orchestrator.js +428 -664
  43. package/src/planning-game/client.js +2 -2
  44. package/src/planning-game/pipeline-adapter.js +1 -1
  45. package/src/prompts/coder.js +17 -3
  46. package/src/prompts/reviewer.js +2 -2
  47. package/src/prompts/rtk-snippet.js +3 -5
  48. package/src/review/diff-generator.js +66 -17
  49. package/src/review/snapshot-diff.js +122 -0
  50. package/src/review/tdd-policy.js +4 -2
  51. package/src/roles/agent-role.js +135 -0
  52. package/src/roles/architect-role.js +61 -108
  53. package/src/roles/audit-role.js +25 -71
  54. package/src/roles/coder-role.js +32 -57
  55. package/src/roles/discover-role.js +30 -71
  56. package/src/roles/hu-reviewer-role.js +51 -99
  57. package/src/roles/impeccable-role.js +33 -88
  58. package/src/roles/index.js +2 -0
  59. package/src/roles/karajan-brain-role.js +134 -0
  60. package/src/roles/planner-role.js +40 -75
  61. package/src/roles/refactorer-role.js +22 -64
  62. package/src/roles/researcher-role.js +51 -115
  63. package/src/roles/reviewer-role.js +68 -125
  64. package/src/roles/security-role.js +38 -102
  65. package/src/roles/solomon-role.js +69 -11
  66. package/src/roles/tester-role.js +121 -95
  67. package/src/roles/triage-role.js +50 -111
  68. package/src/session-store.js +1 -1
  69. package/src/skills/skill-detector.js +11 -0
  70. package/src/sonar/config-resolver.js +20 -1
  71. package/src/sonar/credentials.js +17 -0
  72. package/src/sonar/project-key.js +1 -1
  73. package/src/sonar/scanner.js +1 -1
  74. package/src/utils/display.js +33 -14
  75. package/src/utils/injection-guard.js +23 -8
  76. package/src/utils/logger.js +6 -1
  77. package/src/utils/process.js +3 -4
  78. package/src/utils/role-assigner.js +131 -0
  79. package/src/utils/update-check.js +5 -1
  80. package/src/webperf/devtools-detect.js +70 -23
  81. package/templates/roles/coder.md +7 -0
  82. package/templates/roles/karajan-brain.md +95 -0
  83. package/templates/roles/solomon.md +67 -106
  84. package/templates/workflows/automerge.yml +4 -4
  85. package/templates/workflows/houston-override.yml +4 -4
  86. package/templates/workflows/{becaria-gateway.yml → kj-ci-gateway.yml} +12 -12
  87. package/src/proxy/adapters/anthropic.js +0 -131
  88. package/src/proxy/adapters/gemini.js +0 -106
  89. package/src/proxy/adapters/index.js +0 -51
  90. package/src/proxy/adapters/openai.js +0 -71
  91. package/src/proxy/compression/ai-compressor.js +0 -209
  92. package/src/proxy/compression/dedup-cache.js +0 -100
  93. package/src/proxy/compression/deterministic/bash-build.js +0 -60
  94. package/src/proxy/compression/deterministic/bash-git.js +0 -101
  95. package/src/proxy/compression/deterministic/bash-infra.js +0 -64
  96. package/src/proxy/compression/deterministic/bash-misc.js +0 -53
  97. package/src/proxy/compression/deterministic/bash-pkg.js +0 -49
  98. package/src/proxy/compression/deterministic/bash-test.js +0 -61
  99. package/src/proxy/compression/deterministic/glob.js +0 -26
  100. package/src/proxy/compression/deterministic/grep.js +0 -53
  101. package/src/proxy/compression/deterministic/index.js +0 -58
  102. package/src/proxy/compression/deterministic/read.js +0 -22
  103. package/src/proxy/compression/deterministic/utils.js +0 -33
  104. package/src/proxy/compression/pipeline.js +0 -270
  105. package/src/proxy/middleware/prompt-injector.js +0 -165
  106. package/src/proxy/middleware/response-interceptor.js +0 -223
  107. package/src/proxy/progress-monitor.js +0 -98
  108. package/src/proxy/proxy-lifecycle.js +0 -201
  109. package/src/proxy/proxy-server.js +0 -239
  110. package/src/proxy/security.js +0 -45
  111. /package/src/{becaria → ci}/index.js +0 -0
  112. /package/src/{becaria → ci}/repo.js +0 -0
@@ -1,239 +0,0 @@
1
- import http from "node:http";
2
- import https from "node:https";
3
- import { isRequestTooLarge } from "./security.js";
4
-
5
- /**
6
- * Create a lightweight HTTP forward proxy with middleware pipeline.
7
- *
8
- * The proxy receives plain HTTP on localhost, detects the AI provider from the
9
- * Host header, runs the middleware chain, then forwards the request as HTTPS
10
- * (or HTTP for testing) to the real API endpoint.
11
- *
12
- * @param {object} options
13
- * @param {number} options.port - Port to listen on (0 = OS-assigned)
14
- * @param {Record<string, string>} options.targetHosts - Map host → provider name
15
- * @param {object} [options._testTarget] - Override target for testing (hostname, port, protocol)
16
- * @returns {ProxyServer}
17
- */
18
- export function createProxyServer({ port = 0, targetHosts = {}, _testTarget } = {}) {
19
- const middlewares = [];
20
- const startTime = Date.now();
21
- const stats = { requests: 0, bytes_in: 0, bytes_out: 0 };
22
- let activeConnections = 0;
23
- let closing = false;
24
- let closeResolve = null;
25
-
26
- function detectProvider(host) {
27
- // Strip port from host if present
28
- const hostname = (host || "").split(":")[0];
29
- return targetHosts[hostname] || "unknown";
30
- }
31
-
32
- /**
33
- * Compose middlewares into a single function using Koa-style next() chaining.
34
- */
35
- function compose(mws, finalHandler) {
36
- return function run(ctx) {
37
- let index = -1;
38
- function dispatch(i) {
39
- if (i <= index) return Promise.reject(new Error("next() called multiple times"));
40
- index = i;
41
- const fn = i < mws.length ? mws[i] : finalHandler;
42
- if (!fn) return Promise.resolve();
43
- return Promise.resolve(fn(ctx, () => dispatch(i + 1)));
44
- }
45
- return dispatch(0);
46
- };
47
- }
48
-
49
- /**
50
- * Forward the request to the upstream target and stream the response back.
51
- */
52
- function forwardRequest(ctx) {
53
- return new Promise((resolve, reject) => {
54
- const { req, res } = ctx;
55
- const host = (req.headers.host || "").split(":")[0];
56
- const body = ctx.modifiedBody != null ? ctx.modifiedBody : ctx.body;
57
-
58
- // Determine target
59
- let targetHostname, targetPort, useHttps;
60
- if (_testTarget) {
61
- targetHostname = _testTarget.hostname;
62
- targetPort = _testTarget.port;
63
- useHttps = _testTarget.protocol === "https:";
64
- } else {
65
- targetHostname = host;
66
- targetPort = 443;
67
- useHttps = true;
68
- }
69
-
70
- const transport = useHttps ? https : http;
71
-
72
- // Clone headers, remove hop-by-hop headers, update content-length
73
- const headers = { ...req.headers };
74
- delete headers["transfer-encoding"];
75
- delete headers["connection"];
76
- delete headers["proxy-connection"];
77
- delete headers["keep-alive"];
78
- if (body != null) {
79
- headers["content-length"] = String(Buffer.byteLength(body));
80
- }
81
-
82
- const proxyReq = transport.request(
83
- {
84
- hostname: targetHostname,
85
- port: targetPort,
86
- path: req.url,
87
- method: req.method,
88
- headers,
89
- },
90
- (proxyRes) => {
91
- // Track bytes out by instrumenting the response
92
- proxyRes.on("data", (chunk) => {
93
- stats.bytes_out += chunk.length;
94
- });
95
-
96
- // Stream the response back — write head then pipe
97
- res.writeHead(proxyRes.statusCode, proxyRes.headers);
98
- proxyRes.pipe(res);
99
- proxyRes.on("end", resolve);
100
- proxyRes.on("error", reject);
101
- },
102
- );
103
-
104
- proxyReq.on("error", (err) => {
105
- if (!res.headersSent) {
106
- res.writeHead(502, { "content-type": "application/json" });
107
- res.end(JSON.stringify({ error: "Bad Gateway", message: err.message }));
108
- }
109
- resolve(); // Don't reject — we handled the error via 502
110
- });
111
-
112
- if (body) {
113
- proxyReq.write(body);
114
- }
115
- proxyReq.end();
116
- });
117
- }
118
-
119
- function handleRequest(req, res) {
120
- // Health check — bypass middleware and proxy
121
- if (req.url === "/_kj/health") {
122
- res.writeHead(200, { "content-type": "application/json" });
123
- res.end(
124
- JSON.stringify({
125
- status: "ok",
126
- uptime: Math.round((Date.now() - startTime) / 1000),
127
- requests: stats.requests,
128
- }),
129
- );
130
- return;
131
- }
132
-
133
- // Reject oversized requests early (50 MB default)
134
- const MAX_BODY_BYTES = 50 * 1024 * 1024;
135
- if (isRequestTooLarge(req.headers["content-length"], MAX_BODY_BYTES)) {
136
- res.writeHead(413, { "content-type": "application/json" });
137
- res.end(JSON.stringify({ error: "Payload Too Large", maxBytes: MAX_BODY_BYTES }));
138
- return;
139
- }
140
-
141
- activeConnections++;
142
- stats.requests++;
143
-
144
- // Collect body — enforce streaming size limit even without Content-Length
145
- let bodySize = 0;
146
- const chunks = [];
147
- req.on("data", (c) => {
148
- bodySize += c.length;
149
- if (bodySize > MAX_BODY_BYTES) {
150
- res.writeHead(413, { "content-type": "application/json" });
151
- res.end(JSON.stringify({ error: "Payload Too Large", maxBytes: MAX_BODY_BYTES }));
152
- req.destroy();
153
- return;
154
- }
155
- chunks.push(c);
156
- stats.bytes_in += c.length;
157
- });
158
- req.on("end", () => {
159
- const body = Buffer.concat(chunks).toString();
160
- const provider = detectProvider(req.headers.host);
161
-
162
- const ctx = {
163
- req,
164
- res,
165
- body,
166
- provider,
167
- modifiedBody: null,
168
- };
169
-
170
- const chain = compose(middlewares, (c) => forwardRequest(c));
171
- chain(ctx)
172
- .catch((err) => {
173
- if (!res.headersSent) {
174
- res.writeHead(500, { "content-type": "application/json" });
175
- res.end(JSON.stringify({ error: "Internal Server Error", message: err.message }));
176
- }
177
- })
178
- .finally(() => {
179
- activeConnections--;
180
- if (closing && activeConnections === 0 && closeResolve) {
181
- closeResolve();
182
- }
183
- });
184
- });
185
- }
186
-
187
- const server = http.createServer(handleRequest);
188
-
189
- const instance = {
190
- get port() {
191
- const addr = server.address();
192
- return addr ? addr.port : port;
193
- },
194
-
195
- get stats() {
196
- return { ...stats };
197
- },
198
-
199
- /**
200
- * Register a middleware function.
201
- * @param {(ctx: object, next: () => Promise<void>) => Promise<void>} fn
202
- */
203
- use(fn) {
204
- middlewares.push(fn);
205
- return instance;
206
- },
207
-
208
- /**
209
- * Start listening on the configured port.
210
- * @returns {Promise<void>}
211
- */
212
- listen() {
213
- return new Promise((resolve, reject) => {
214
- server.on("error", reject);
215
- server.listen(port, "127.0.0.1", () => resolve());
216
- });
217
- },
218
-
219
- /**
220
- * Graceful shutdown — stop accepting new connections,
221
- * drain active ones, then resolve.
222
- * @returns {Promise<void>}
223
- */
224
- close() {
225
- closing = true;
226
- return new Promise((resolve) => {
227
- server.close(() => {
228
- if (activeConnections === 0) {
229
- resolve();
230
- } else {
231
- closeResolve = resolve;
232
- }
233
- });
234
- });
235
- },
236
- };
237
-
238
- return instance;
239
- }
@@ -1,45 +0,0 @@
1
- /**
2
- * Security utilities for the proxy layer.
3
- *
4
- * - sanitizeHeaders: strips sensitive values before logging
5
- * - isRequestTooLarge: checks Content-Length against a limit
6
- *
7
- * @module proxy/security
8
- */
9
-
10
- /** Headers whose values must never appear in logs. */
11
- const SENSITIVE_HEADERS = new Set([
12
- "authorization",
13
- "x-api-key",
14
- "x-goog-api-key",
15
- ]);
16
-
17
- /**
18
- * Return a shallow copy of `headers` with sensitive values replaced by "[REDACTED]".
19
- *
20
- * @param {Record<string, string>} headers
21
- * @returns {Record<string, string>}
22
- */
23
- export function sanitizeHeaders(headers) {
24
- if (!headers || typeof headers !== "object") return {};
25
-
26
- const result = {};
27
- for (const [key, value] of Object.entries(headers)) {
28
- result[key] = SENSITIVE_HEADERS.has(key.toLowerCase()) ? "[REDACTED]" : value;
29
- }
30
- return result;
31
- }
32
-
33
- /**
34
- * Check whether the declared Content-Length exceeds the allowed maximum.
35
- *
36
- * @param {number|string|undefined} contentLength - Value of the content-length header
37
- * @param {number} maxBytes - Maximum allowed body size in bytes (default 50 MB)
38
- * @returns {boolean} true if the request is too large
39
- */
40
- export function isRequestTooLarge(contentLength, maxBytes = 50 * 1024 * 1024) {
41
- if (contentLength == null) return false;
42
- const len = Number(contentLength);
43
- if (Number.isNaN(len)) return false;
44
- return len > maxBytes;
45
- }
File without changes
File without changes