karajan-code 1.59.0 → 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 (115) 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 +34 -6
  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 +3 -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/domains/domain-loader.js +85 -4
  21. package/src/git/automation.js +9 -9
  22. package/src/guards/output-guard.js +0 -17
  23. package/src/mcp/handlers/direct-handlers.js +1 -1
  24. package/src/mcp/handlers/run-handler.js +1 -2
  25. package/src/mcp/sovereignty-guard.js +2 -2
  26. package/src/mcp/tools.js +3 -2
  27. package/src/orchestrator/brain-coordinator.js +165 -0
  28. package/src/orchestrator/ci-integration.js +139 -0
  29. package/src/orchestrator/config-init.js +435 -0
  30. package/src/orchestrator/direct-actions.js +186 -0
  31. package/src/orchestrator/feedback-enrichment.js +140 -0
  32. package/src/orchestrator/feedback-queue.js +152 -0
  33. package/src/orchestrator/flow-control.js +159 -0
  34. package/src/orchestrator/post-loop-stages.js +20 -15
  35. package/src/orchestrator/preflight-checks.js +7 -3
  36. package/src/orchestrator/role-output-compressor.js +190 -0
  37. package/src/orchestrator/session-journal.js +344 -0
  38. package/src/orchestrator/solomon-escalation.js +38 -5
  39. package/src/orchestrator/solomon-rules.js +32 -33
  40. package/src/orchestrator/stages/reviewer-stage.js +16 -5
  41. package/src/orchestrator/stages/sonar-stage.js +24 -1
  42. package/src/orchestrator/verification-gate.js +146 -0
  43. package/src/orchestrator.js +435 -661
  44. package/src/planning-game/client.js +2 -2
  45. package/src/planning-game/pipeline-adapter.js +1 -1
  46. package/src/prompts/coder.js +17 -3
  47. package/src/prompts/reviewer.js +2 -2
  48. package/src/prompts/rtk-snippet.js +3 -5
  49. package/src/review/diff-generator.js +66 -17
  50. package/src/review/snapshot-diff.js +122 -0
  51. package/src/review/tdd-policy.js +4 -2
  52. package/src/roles/agent-role.js +135 -0
  53. package/src/roles/architect-role.js +61 -108
  54. package/src/roles/audit-role.js +25 -71
  55. package/src/roles/coder-role.js +32 -57
  56. package/src/roles/discover-role.js +30 -71
  57. package/src/roles/hu-reviewer-role.js +51 -99
  58. package/src/roles/impeccable-role.js +34 -91
  59. package/src/roles/index.js +2 -0
  60. package/src/roles/karajan-brain-role.js +134 -0
  61. package/src/roles/planner-role.js +40 -75
  62. package/src/roles/refactorer-role.js +22 -64
  63. package/src/roles/researcher-role.js +52 -118
  64. package/src/roles/reviewer-role.js +69 -126
  65. package/src/roles/security-role.js +38 -102
  66. package/src/roles/solomon-role.js +71 -15
  67. package/src/roles/tester-role.js +121 -95
  68. package/src/roles/triage-role.js +50 -111
  69. package/src/session-store.js +1 -1
  70. package/src/skills/skill-detector.js +11 -0
  71. package/src/sonar/config-resolver.js +20 -1
  72. package/src/sonar/credentials.js +17 -0
  73. package/src/sonar/project-key.js +1 -1
  74. package/src/sonar/scanner.js +1 -1
  75. package/src/utils/banner.js +66 -0
  76. package/src/utils/display.js +35 -17
  77. package/src/utils/injection-guard.js +23 -8
  78. package/src/utils/logger.js +6 -1
  79. package/src/utils/process.js +3 -4
  80. package/src/utils/role-assigner.js +131 -0
  81. package/src/utils/update-check.js +5 -1
  82. package/src/utils/welcome.js +3 -8
  83. package/src/webperf/devtools-detect.js +70 -23
  84. package/templates/roles/coder.md +7 -0
  85. package/templates/roles/karajan-brain.md +95 -0
  86. package/templates/roles/solomon.md +67 -106
  87. package/templates/workflows/automerge.yml +4 -4
  88. package/templates/workflows/houston-override.yml +4 -4
  89. package/templates/workflows/{becaria-gateway.yml → kj-ci-gateway.yml} +12 -12
  90. package/src/proxy/adapters/anthropic.js +0 -131
  91. package/src/proxy/adapters/gemini.js +0 -106
  92. package/src/proxy/adapters/index.js +0 -51
  93. package/src/proxy/adapters/openai.js +0 -71
  94. package/src/proxy/compression/ai-compressor.js +0 -209
  95. package/src/proxy/compression/dedup-cache.js +0 -100
  96. package/src/proxy/compression/deterministic/bash-build.js +0 -60
  97. package/src/proxy/compression/deterministic/bash-git.js +0 -101
  98. package/src/proxy/compression/deterministic/bash-infra.js +0 -64
  99. package/src/proxy/compression/deterministic/bash-misc.js +0 -53
  100. package/src/proxy/compression/deterministic/bash-pkg.js +0 -49
  101. package/src/proxy/compression/deterministic/bash-test.js +0 -61
  102. package/src/proxy/compression/deterministic/glob.js +0 -26
  103. package/src/proxy/compression/deterministic/grep.js +0 -53
  104. package/src/proxy/compression/deterministic/index.js +0 -58
  105. package/src/proxy/compression/deterministic/read.js +0 -22
  106. package/src/proxy/compression/deterministic/utils.js +0 -33
  107. package/src/proxy/compression/pipeline.js +0 -270
  108. package/src/proxy/middleware/prompt-injector.js +0 -165
  109. package/src/proxy/middleware/response-interceptor.js +0 -223
  110. package/src/proxy/progress-monitor.js +0 -98
  111. package/src/proxy/proxy-lifecycle.js +0 -198
  112. package/src/proxy/proxy-server.js +0 -239
  113. package/src/proxy/security.js +0 -45
  114. /package/src/{becaria → ci}/index.js +0 -0
  115. /package/src/{becaria → ci}/repo.js +0 -0
@@ -7,24 +7,24 @@ import { resolveReviewProfile } from "../review/profiles.js";
7
7
 
8
8
  export async function reviewCommand({ task, config, logger, baseRef }) {
9
9
  const reviewerRole = resolveRole(config, "reviewer");
10
- await assertAgentsAvailable([reviewerRole.provider, config.reviewer_options?.fallback_reviewer]);
10
+ await assertAgentsAvailable([reviewerRole.provider]);
11
11
  logger.info(`Reviewer (${reviewerRole.provider}) starting...`);
12
12
  const reviewer = createAgent(reviewerRole.provider, config, logger);
13
13
 
14
14
  let diff;
15
- if (config.becaria?.enabled) {
16
- // BecarIA mode: read diff from open PR
17
- const { detectRepo, detectPrNumber } = await import("../becaria/repo.js");
18
- const { getPrDiff } = await import("../becaria/pr-diff.js");
15
+ if (config.ci?.enabled) {
16
+ // CI mode: read diff from open PR
17
+ const { detectRepo, detectPrNumber } = await import("../ci/repo.js");
18
+ const { getPrDiff } = await import("../ci/pr-diff.js");
19
19
  const repo = await detectRepo();
20
20
  const prNumber = await detectPrNumber();
21
21
  if (!prNumber) {
22
- throw new Error("BecarIA enabled but no open PR found for current branch. Create a PR first or disable BecarIA.");
22
+ throw new Error("CI enabled but no open PR found for current branch. Create a PR first or disable CI.");
23
23
  }
24
- logger.info(`BecarIA: reading PR diff #${prNumber}`);
24
+ logger.info(`CI: reading PR diff #${prNumber}`);
25
25
  diff = await getPrDiff(prNumber);
26
26
  // Store for dispatch later
27
- config._becaria_pr = { repo, prNumber };
27
+ config._ci_pr = { repo, prNumber };
28
28
  } else {
29
29
  const resolvedBase = await computeBaseRef({ baseBranch: config.base_branch, baseRef });
30
30
  diff = await generateDiff({ baseRef: resolvedBase });
@@ -42,12 +42,12 @@ export async function reviewCommand({ task, config, logger, baseRef }) {
42
42
  console.log(result.output);
43
43
  logger.info(`Reviewer completed (exit ${result.exitCode})`);
44
44
 
45
- // BecarIA: dispatch review result
46
- if (config.becaria?.enabled && config._becaria_pr) {
45
+ // CI: dispatch review result
46
+ if (config.ci?.enabled && config._ci_pr) {
47
47
  try {
48
- const { dispatchReview, dispatchComment } = await import("../becaria/dispatch.js");
49
- const { repo, prNumber } = config._becaria_pr;
50
- const bc = config.becaria;
48
+ const { dispatchReview, dispatchComment } = await import("../ci/dispatch.js");
49
+ const { repo, prNumber } = config._ci_pr;
50
+ const bc = config.ci;
51
51
 
52
52
  // Try to parse structured review from output
53
53
  let review;
@@ -61,18 +61,18 @@ export async function reviewCommand({ task, config, logger, baseRef }) {
61
61
  await dispatchReview({
62
62
  repo, prNumber, event,
63
63
  body: review.summary || result.output.slice(0, 500),
64
- agent: "Reviewer", becariaConfig: bc
64
+ agent: "Reviewer", ciConfig: bc
65
65
  });
66
66
 
67
67
  await dispatchComment({
68
68
  repo, prNumber, agent: "Reviewer",
69
69
  body: `Standalone review: ${event}\n\n${review.summary || result.output.slice(0, 1000)}`,
70
- becariaConfig: bc
70
+ ciConfig: bc
71
71
  });
72
72
 
73
- logger.info(`BecarIA: dispatched review for PR #${prNumber}`);
73
+ logger.info(`CI: dispatched review for PR #${prNumber}`);
74
74
  } catch (err) {
75
- logger.warn(`BecarIA dispatch failed (non-blocking): ${err.message}`);
75
+ logger.warn(`CI dispatch failed (non-blocking): ${err.message}`);
76
76
  }
77
77
  }
78
78
  }
@@ -35,8 +35,7 @@ export async function runCommandHandler({ task, config, logger, flags }) {
35
35
  } catch { /* non-blocking */ }
36
36
 
37
37
  const requiredProviders = [
38
- resolveRole(config, "coder").provider,
39
- config.reviewer_options?.fallback_reviewer
38
+ resolveRole(config, "coder").provider
40
39
  ];
41
40
  if (config.pipeline?.reviewer?.enabled !== false) {
42
41
  requiredProviders.push(resolveRole(config, "reviewer").provider);
@@ -5,7 +5,7 @@
5
5
  import { readRunLog } from "../utils/run-log.js";
6
6
  import { loadMostRecentSession } from "../session-store.js";
7
7
  import { buildDashboard } from "../utils/status-dashboard.js";
8
- import { getProxyStats } from "../proxy/proxy-lifecycle.js";
8
+
9
9
 
10
10
  /**
11
11
  * Run the `kj status` command: print a human-readable dashboard to stdout.
@@ -33,10 +33,4 @@ export async function statusCommand({ lines = 50, projectDir } = {}) {
33
33
 
34
34
  const dashboard = buildDashboard(session, logResult.lines || [], { stories });
35
35
  console.log(dashboard);
36
-
37
- const proxyStats = getProxyStats();
38
- if (proxyStats) {
39
- console.log("");
40
- console.log(`Proxy: port ${proxyStats.port}, ${proxyStats.requests} requests, ${proxyStats.bytes_in} bytes in, ${proxyStats.bytes_out} bytes out`);
41
- }
42
36
  }
package/src/config.js CHANGED
@@ -49,7 +49,7 @@ const DEFAULTS = {
49
49
  model: null,
50
50
  deterministic: true,
51
51
  retries: 1,
52
- fallback_reviewer: "codex"
52
+ fallback_reviewer: null
53
53
  },
54
54
  development: {
55
55
  methodology: "tdd",
@@ -128,7 +128,7 @@ const DEFAULTS = {
128
128
  policies: {},
129
129
  serena: { enabled: false },
130
130
  planning_game: { enabled: false, project_id: null, codeveloper: null },
131
- becaria: { enabled: false, review_event: "becaria-review", comment_event: "becaria-comment", comment_prefix: true },
131
+ ci: { enabled: false, review_event: "kj-review", comment_event: "kj-comment", comment_prefix: true },
132
132
  git: { auto_commit: false, auto_push: false, auto_pr: false, auto_rebase: true, branch_prefix: "feat/" },
133
133
  output: { report_dir: "./.reviews", log_level: "info", quiet: true },
134
134
  budget: {
@@ -159,50 +159,12 @@ const DEFAULTS = {
159
159
  failFast: {
160
160
  repeatThreshold: 2
161
161
  },
162
- retry: {
163
- max_attempts: 3,
164
- initial_backoff_ms: 1000,
165
- max_backoff_ms: 30000,
166
- backoff_multiplier: 2,
167
- jitter_factor: 0.1
168
- },
169
162
  webperf: {
170
163
  enabled: true,
171
164
  devtools_mcp: false,
172
165
  thresholds: { lcp: 2500, cls: 0.1, inp: 200 }
173
166
  },
174
167
  telemetry: true,
175
- proxy: {
176
- enabled: true,
177
- port: "auto",
178
- compression: {
179
- enabled: true,
180
- ai_compression: false,
181
- ai_model: "haiku",
182
- ai_provider: "anthropic",
183
- layers: {
184
- git: true,
185
- tests: true,
186
- build: true,
187
- infra: true,
188
- packages: true,
189
- read_dedup: true,
190
- glob_truncate: true,
191
- grep_collapse: true,
192
- },
193
- pressure_thresholds: {
194
- low: 0.5,
195
- medium: 0.8,
196
- high: 0.9,
197
- },
198
- },
199
- cache: {
200
- persist_to_disk: true,
201
- flush_interval_ms: 5000,
202
- },
203
- inject_prompts: true,
204
- monitor: true,
205
- },
206
168
  guards: {
207
169
  output: {
208
170
  enabled: true,
@@ -398,12 +360,12 @@ function applyMethodologyOverride(out, flags) {
398
360
  out.development.require_test_changes = methodology === "tdd";
399
361
  }
400
362
 
401
- function applyBecariaOverride(out, flags) {
402
- out.becaria = out.becaria || { enabled: false };
403
- if (flags.enableBecaria === undefined) return;
404
- out.becaria.enabled = Boolean(flags.enableBecaria);
405
- // BecarIA requires git automation (commit + push + PR)
406
- if (out.becaria.enabled) {
363
+ function applyCiOverride(out, flags) {
364
+ out.ci = out.ci || { enabled: false };
365
+ if (flags.enableCi === undefined) return;
366
+ out.ci.enabled = Boolean(flags.enableCi);
367
+ // CI requires git automation (commit + push + PR)
368
+ if (out.ci.enabled) {
407
369
  out.git.auto_commit = true;
408
370
  out.git.auto_push = true;
409
371
  out.git.auto_pr = true;
@@ -432,10 +394,6 @@ function applyMiscOverrides(out, flags) {
432
394
  if (flags.pgTask) out.planning_game.enabled = true;
433
395
  if (flags.pgProject) out.planning_game.project_id = flags.pgProject;
434
396
 
435
- out.proxy = out.proxy || {};
436
- if (flags.noProxy) out.proxy.enabled = false;
437
- if (flags.proxyPort) out.proxy.port = flags.proxyPort;
438
-
439
397
  out.model_selection = out.model_selection || { enabled: true, tiers: {}, role_overrides: {} };
440
398
  if (flags.smartModels === true) out.model_selection.enabled = true;
441
399
  if (flags.smartModels === false || flags.noSmartModels === true) out.model_selection.enabled = false;
@@ -461,7 +419,7 @@ export function applyRunOverrides(config, flags) {
461
419
  applyPipelineOverrides(out, flags);
462
420
  applyScalarAndBooleanOverrides(out, flags);
463
421
  applyMethodologyOverride(out, flags);
464
- applyBecariaOverride(out, flags);
422
+ applyCiOverride(out, flags);
465
423
  applyMiscOverrides(out, flags);
466
424
  applyOutputModeOverrides(out, flags);
467
425
 
@@ -479,7 +437,7 @@ const AGENT_MODEL_SIGNATURES = {
479
437
  gemini: ["gemini", "flash-"]
480
438
  };
481
439
 
482
- export function isModelCompatible(agent, model) {
440
+ function isModelCompatible(agent, model) {
483
441
  if (!model || !agent) return true;
484
442
  const lower = model.toLowerCase();
485
443
 
@@ -5,8 +5,8 @@
5
5
  * Project-local domains override user-global domains by directory name.
6
6
  */
7
7
 
8
- import { readdir, readFile } from "node:fs/promises";
9
- import { join } from "node:path";
8
+ import { readdir, readFile, mkdir, writeFile, stat } from "node:fs/promises";
9
+ import { join, isAbsolute } from "node:path";
10
10
  import yaml from "js-yaml";
11
11
  import { getKarajanHome } from "../utils/paths.js";
12
12
 
@@ -103,8 +103,52 @@ function parseSections(content) {
103
103
  return sections;
104
104
  }
105
105
 
106
+ /**
107
+ * Auto-detect project context files (README.md, CLAUDE.md) as lightweight domains.
108
+ * Used as fallback when no explicit .karajan/domains/ are configured.
109
+ *
110
+ * @param {string} projectDir — absolute path to project root
111
+ * @returns {Promise<DomainFile[]>}
112
+ */
113
+ export async function autoDetectDomains(projectDir) {
114
+ if (!projectDir) return [];
115
+
116
+ const candidates = ["README.md", "CLAUDE.md", "docs/README.md"];
117
+ const detected = [];
118
+
119
+ for (const rel of candidates) {
120
+ const filePath = join(projectDir, rel);
121
+ let content;
122
+ try {
123
+ content = await readFile(filePath, "utf-8");
124
+ } catch { continue; }
125
+
126
+ if (!content || content.trim().length < 20) continue;
127
+
128
+ // Truncate to first 4000 chars to avoid bloating context
129
+ const trimmed = content.length > 4000 ? content.slice(0, 4000) + "\n\n[...truncated]" : content;
130
+
131
+ detected.push({
132
+ name: `auto:${rel}`,
133
+ description: `Auto-detected from ${rel}`,
134
+ tags: [],
135
+ version: "0.0.0",
136
+ author: "",
137
+ visibility: "private",
138
+ sources: [{ type: "auto-detect", note: rel }],
139
+ content: trimmed,
140
+ sections: parseSections(trimmed),
141
+ filePath,
142
+ origin: "auto-detect"
143
+ });
144
+ }
145
+
146
+ return detected;
147
+ }
148
+
106
149
  /**
107
150
  * Load all domains from project-local and user-global directories.
151
+ * Falls back to auto-detecting README.md/CLAUDE.md when no explicit domains found.
108
152
  * Project-local domains override user-global domains when directory names match.
109
153
  *
110
154
  * @param {string|null} projectDir — absolute path to project root (null = user-global only)
@@ -119,7 +163,6 @@ export async function loadDomains(projectDir) {
119
163
  const projectDomains = projectDomainDir ? await scanDomainDir(projectDomainDir, "project") : [];
120
164
 
121
165
  // Build a map keyed by directory name for merge.
122
- // We track by directory name (not domain name) because the override is by directory.
123
166
  const merged = new Map();
124
167
 
125
168
  for (const { dirName, domain } of userDomains) {
@@ -129,7 +172,14 @@ export async function loadDomains(projectDir) {
129
172
  merged.set(dirName, domain); // project overrides user
130
173
  }
131
174
 
132
- return Array.from(merged.values());
175
+ const explicit = Array.from(merged.values());
176
+
177
+ // Fallback: auto-detect README.md, CLAUDE.md when no explicit domains found
178
+ if (explicit.length === 0 && projectDir) {
179
+ return autoDetectDomains(projectDir);
180
+ }
181
+
182
+ return explicit;
133
183
  }
134
184
 
135
185
  /**
@@ -162,3 +212,34 @@ async function scanDomainDir(dir, origin) {
162
212
 
163
213
  return results;
164
214
  }
215
+
216
+ /**
217
+ * Persist an inline domain (text or file path) to .karajan/domains/inline/DOMAIN.md.
218
+ * If domainInput is a path to an existing .md file, reads its content.
219
+ * Otherwise treats it as inline text.
220
+ *
221
+ * @param {string} domainInput — inline text or absolute path to a .md file
222
+ * @param {string} projectDir — project root
223
+ */
224
+ export async function persistInlineDomain(domainInput, projectDir) {
225
+ if (!domainInput || !projectDir) return;
226
+
227
+ let content = domainInput;
228
+
229
+ // If it looks like a file path, try to read it
230
+ if (isAbsolute(domainInput) && domainInput.endsWith(".md")) {
231
+ try {
232
+ const s = await stat(domainInput);
233
+ if (s.isFile()) {
234
+ content = await readFile(domainInput, "utf-8");
235
+ }
236
+ } catch { /* treat as inline text */ }
237
+ }
238
+
239
+ const domainDir = join(projectDir, ".karajan", "domains", "inline");
240
+ await mkdir(domainDir, { recursive: true });
241
+
242
+ const domainFile = join(domainDir, DOMAIN_FILE);
243
+ const wrapped = `---\nname: inline-domain\ndescription: Domain knowledge provided via --domain parameter\ntags: []\nversion: "0.0.0"\n---\n\n${content}\n`;
244
+ await writeFile(domainFile, wrapped, "utf-8");
245
+ }
@@ -110,7 +110,7 @@ export function buildPrBody({ task, stageResults }) {
110
110
  }
111
111
 
112
112
  /**
113
- * Create an early PR after the first coder iteration (BecarIA Gateway flow).
113
+ * Create an early PR after the first coder iteration (Karajan CI flow).
114
114
  * Commits, pushes, and creates a PR before the reviewer runs.
115
115
  * Returns { prNumber, prUrl, commits } or null if nothing to commit.
116
116
  */
@@ -125,10 +125,10 @@ export async function earlyPrCreation({ gitCtx, task, logger, session, stageResu
125
125
  }
126
126
 
127
127
  const commits = commitResult.commit ? [commitResult.commit] : [];
128
- await addCheckpoint(session, { stage: "becaria-commit", committed: true });
128
+ await addCheckpoint(session, { stage: "ci-commit", committed: true });
129
129
 
130
130
  await pushBranch(gitCtx.branch);
131
- await addCheckpoint(session, { stage: "becaria-push", branch: gitCtx.branch });
131
+ await addCheckpoint(session, { stage: "ci-push", branch: gitCtx.branch });
132
132
  logger.info(`Pushed branch for early PR: ${gitCtx.branch}`);
133
133
 
134
134
  const body = buildPrBody({ task, stageResults });
@@ -138,7 +138,7 @@ export async function earlyPrCreation({ gitCtx, task, logger, session, stageResu
138
138
  title: commitMessageFromTask(task),
139
139
  body
140
140
  });
141
- await addCheckpoint(session, { stage: "becaria-pr", branch: gitCtx.branch, pr: prUrl });
141
+ await addCheckpoint(session, { stage: "ci-pr", branch: gitCtx.branch, pr: prUrl });
142
142
  logger.info(`Early PR created: ${prUrl}`);
143
143
 
144
144
  // Extract PR number from URL (e.g. https://github.com/owner/repo/pull/42)
@@ -147,7 +147,7 @@ export async function earlyPrCreation({ gitCtx, task, logger, session, stageResu
147
147
  }
148
148
 
149
149
  /**
150
- * Incremental push after each coder iteration (BecarIA Gateway flow).
150
+ * Incremental push after each coder iteration (Karajan CI flow).
151
151
  * Commits and pushes without creating a new PR.
152
152
  */
153
153
  export async function incrementalPush({ gitCtx, task, logger, session }) {
@@ -161,10 +161,10 @@ export async function incrementalPush({ gitCtx, task, logger, session }) {
161
161
  }
162
162
 
163
163
  const commits = commitResult.commit ? [commitResult.commit] : [];
164
- await addCheckpoint(session, { stage: "becaria-incremental-commit", committed: true });
164
+ await addCheckpoint(session, { stage: "ci-incremental-commit", committed: true });
165
165
 
166
166
  await pushBranch(gitCtx.branch);
167
- await addCheckpoint(session, { stage: "becaria-incremental-push", branch: gitCtx.branch });
167
+ await addCheckpoint(session, { stage: "ci-incremental-push", branch: gitCtx.branch });
168
168
  logger.info(`Incremental push: ${gitCtx.branch}`);
169
169
 
170
170
  return { commits };
@@ -202,7 +202,7 @@ export async function finalizeGitAutomation({ config, gitCtx, task, logger, sess
202
202
  logger.info(`Pushed branch: ${gitCtx.branch}`);
203
203
  }
204
204
 
205
- let prUrl = session.becaria_pr_url || null;
205
+ let prUrl = session.ci_pr_url || null;
206
206
  if (config.git.auto_pr && !prUrl) {
207
207
  const body = buildPrBody({ task, stageResults });
208
208
  prUrl = await createPullRequest({
@@ -214,7 +214,7 @@ export async function finalizeGitAutomation({ config, gitCtx, task, logger, sess
214
214
  await addCheckpoint(session, { stage: "git-pr", branch: gitCtx.branch, pr: prUrl });
215
215
  logger.info("Pull request created");
216
216
  } else if (prUrl) {
217
- logger.info(`PR already exists (BecarIA flow): ${prUrl}`);
217
+ logger.info(`PR already exists (CI flow): ${prUrl}`);
218
218
  }
219
219
 
220
220
  return { committed, branch: gitCtx.branch, prUrl, pr: prUrl, commits };
@@ -148,21 +148,4 @@ export function scanDiff(diff, config = {}) {
148
148
  return { pass: !hasCritical, violations };
149
149
  }
150
150
 
151
- /**
152
- * Run output guard on the current git diff.
153
- * This is the main entry point for the pipeline integration.
154
- */
155
- export async function runOutputGuard(config = {}, baseBranch = "main") {
156
- const diffResult = await runCommand("git", ["diff", `origin/${baseBranch}...HEAD`]);
157
- if (diffResult.exitCode !== 0) {
158
- // Fallback: diff against HEAD~1
159
- const fallback = await runCommand("git", ["diff", "HEAD~1"]);
160
- if (fallback.exitCode !== 0) {
161
- return { pass: true, violations: [], error: "Could not generate diff" };
162
- }
163
- return scanDiff(fallback.stdout, config);
164
- }
165
- return scanDiff(diffResult.stdout, config);
166
- }
167
-
168
151
  export { DESTRUCTIVE_PATTERNS, CREDENTIAL_PATTERNS, DEFAULT_PROTECTED_FILES };
@@ -258,7 +258,7 @@ export async function handleReviewDirect(a, server, extra) {
258
258
  const logger = createLogger(config.output.log_level, "mcp");
259
259
 
260
260
  const reviewerRole = resolveRole(config, "reviewer");
261
- await assertAgentsAvailable([reviewerRole.provider, config.reviewer_options?.fallback_reviewer]);
261
+ await assertAgentsAvailable([reviewerRole.provider]);
262
262
 
263
263
  const projectDir = await resolveProjectDir(server, a.projectDir);
264
264
  const runLog = createRunLog(projectDir);
@@ -98,8 +98,7 @@ const PIPELINE_PROVIDER_ROLES = [
98
98
 
99
99
  function collectRequiredProviders(config) {
100
100
  const providers = [
101
- resolveRole(config, "coder").provider,
102
- config.reviewer_options?.fallback_reviewer
101
+ resolveRole(config, "coder").provider
103
102
  ];
104
103
  if (config.pipeline?.reviewer?.enabled !== false) {
105
104
  providers.push(resolveRole(config, "reviewer").provider);
@@ -17,14 +17,14 @@ const ALLOWED_PARAMS = new Set([
17
17
  "enableTester", "enableSecurity", "enableImpeccable",
18
18
  "enableDiscover", "enableArchitect",
19
19
  "architectModel", "huFile",
20
- "enableSerena", "enableBecaria",
20
+ "enableSerena", "enableCi",
21
21
  "reviewerFallback", "reviewerRetries",
22
22
  "mode", "maxIterations", "maxIterationMinutes", "maxTotalMinutes",
23
23
  "baseBranch", "baseRef", "methodology",
24
24
  "autoCommit", "autoPush", "autoPr", "autoRebase", "branchPrefix",
25
25
  "autoSimplify", "smartModels", "checkpointInterval",
26
26
  "taskType", "quiet", "noSonar", "enableSonarcloud",
27
- "kjHome", "sonarToken", "timeoutMs",
27
+ "kjHome", "sonarToken", "timeoutMs", "domain",
28
28
  // Sovereign flags — validated below but still "known"
29
29
  "enableTriage", "enableHuReviewer",
30
30
  ]);
package/src/mcp/tools.js CHANGED
@@ -79,7 +79,7 @@ export const tools = [
79
79
  architectModel: { type: "string" },
80
80
  huFile: { type: "string", description: "Path to YAML file with HU stories to certify before coding" },
81
81
  enableSerena: { type: "boolean" },
82
- enableBecaria: { type: "boolean", description: "Enable BecarIA Gateway (early PR + dispatch comments/reviews)" },
82
+ enableCi: { type: "boolean", description: "Enable Karajan CI (early PR + dispatch comments/reviews)" },
83
83
  reviewerFallback: { type: "string" },
84
84
  reviewerRetries: { type: "number" },
85
85
  mode: { type: "string" },
@@ -104,7 +104,8 @@ export const tools = [
104
104
  enableSonarcloud: { type: "boolean", description: "Enable SonarCloud scan (complementary to SonarQube)" },
105
105
  kjHome: { type: "string" },
106
106
  sonarToken: { type: "string" },
107
- timeoutMs: { type: "number" }
107
+ timeoutMs: { type: "number" },
108
+ domain: { type: "string", description: "Domain knowledge: inline text describing the project domain, or absolute path to a .md file. Auto-saved to .karajan/domains/ for the curator." }
108
109
  }
109
110
  }
110
111
  },
@@ -0,0 +1,165 @@
1
+ // Brain Coordinator: integrates Karajan Brain + 5 support modules into the pipeline.
2
+ // Replaces ad-hoc role-to-role communication with a central intelligence.
3
+
4
+ import * as queue from "./feedback-queue.js";
5
+ import * as enrich from "./feedback-enrichment.js";
6
+ import { verifyCoderOutput, VerificationTracker } from "./verification-gate.js";
7
+ import { executeActions } from "./direct-actions.js";
8
+ import { compressRoleOutput, measureCompression } from "./role-output-compressor.js";
9
+
10
+ /**
11
+ * @typedef {Object} BrainContext
12
+ * @property {Object} feedbackQueue - structured feedback queue
13
+ * @property {VerificationTracker} verificationTracker - tracks coder changes
14
+ * @property {Object} compressionStats - token savings per role
15
+ * @property {boolean} enabled - whether brain coordination is active
16
+ */
17
+
18
+ /**
19
+ * Create a new brain context for a pipeline session.
20
+ */
21
+ export function createBrainContext({ enabled = false } = {}) {
22
+ return {
23
+ feedbackQueue: queue.createQueue(),
24
+ verificationTracker: new VerificationTracker(),
25
+ compressionStats: { totalSaved: 0, perRole: {} },
26
+ enabled
27
+ };
28
+ }
29
+
30
+ /**
31
+ * Check if brain coordination is enabled for this config.
32
+ */
33
+ export function isBrainEnabled(config) {
34
+ return Boolean(config?.brain?.enabled) || Boolean(config?.pipeline?.brain?.enabled);
35
+ }
36
+
37
+ /**
38
+ * After a role completes, push its feedback to the queue and compress.
39
+ * Returns the compressed output for passing to next role.
40
+ */
41
+ export function processRoleOutput(ctx, { roleName, output, iteration }) {
42
+ if (!ctx.enabled) return output;
43
+
44
+ // Compress for next-role context
45
+ const compressed = compressRoleOutput(roleName, output);
46
+ const stats = measureCompression(output, compressed);
47
+ ctx.compressionStats.totalSaved += stats.savedTokens;
48
+ ctx.compressionStats.perRole[roleName] = stats;
49
+
50
+ // Extract feedback entries from specific role outputs
51
+ const entries = extractFeedbackEntries(roleName, output, iteration);
52
+ if (entries.length > 0) {
53
+ queue.addEntries(ctx.feedbackQueue, entries);
54
+ enrich.enrichQueue(ctx.feedbackQueue);
55
+ queue.deduplicate(ctx.feedbackQueue);
56
+ queue.prioritize(ctx.feedbackQueue);
57
+ }
58
+
59
+ return compressed;
60
+ }
61
+
62
+ /**
63
+ * Extract structured feedback entries from a role's output.
64
+ */
65
+ function extractFeedbackEntries(roleName, output, iteration) {
66
+ if (!output || typeof output !== "object") return [];
67
+
68
+ const entries = [];
69
+ if (roleName === "reviewer" && output.blocking_issues) {
70
+ for (const issue of output.blocking_issues) {
71
+ entries.push({
72
+ source: "reviewer",
73
+ severity: issue.severity || "medium",
74
+ description: issue.description || "",
75
+ file: issue.file || null,
76
+ line: issue.line || null,
77
+ suggestedFix: issue.suggested_fix || null,
78
+ id: issue.id || null,
79
+ iteration
80
+ });
81
+ }
82
+ } else if (roleName === "tester" && output.verdict === "fail") {
83
+ const missing = output.missing_scenarios || [];
84
+ for (const scenario of missing) {
85
+ entries.push({
86
+ source: "tester",
87
+ severity: "high",
88
+ category: "tests",
89
+ description: `Missing scenario: ${scenario}`,
90
+ iteration
91
+ });
92
+ }
93
+ if (output.coverage?.overall != null && output.coverage.overall < 80) {
94
+ entries.push({
95
+ source: "tester",
96
+ severity: "high",
97
+ category: "tests",
98
+ description: `Coverage below 80% (${output.coverage.overall}%)`,
99
+ iteration
100
+ });
101
+ }
102
+ } else if (roleName === "security" && output.verdict === "fail") {
103
+ for (const vuln of output.vulnerabilities || []) {
104
+ entries.push({
105
+ source: "security",
106
+ severity: vuln.severity || "high",
107
+ category: "security",
108
+ description: vuln.description || "",
109
+ file: vuln.file || null,
110
+ line: vuln.line || null,
111
+ suggestedFix: vuln.fix_suggestion || null,
112
+ iteration
113
+ });
114
+ }
115
+ }
116
+ return entries;
117
+ }
118
+
119
+ /**
120
+ * Before coder runs, format the feedback queue as an actionable prompt.
121
+ */
122
+ export function buildCoderFeedbackPrompt(ctx) {
123
+ if (!ctx.enabled || ctx.feedbackQueue.entries.length === 0) return null;
124
+ return enrich.formatEnrichedForCoder(ctx.feedbackQueue.entries);
125
+ }
126
+
127
+ /**
128
+ * After coder runs, verify changes were made. Returns verification result.
129
+ */
130
+ export function verifyCoderRan(ctx, { baseRef, projectDir }) {
131
+ if (!ctx.enabled) return { passed: true, filesChanged: 0, reason: "brain disabled" };
132
+ const result = verifyCoderOutput({ baseRef, projectDir });
133
+ ctx.verificationTracker.record(result);
134
+ return result;
135
+ }
136
+
137
+ /**
138
+ * Execute direct actions (npm install, gitignore updates, etc.).
139
+ */
140
+ export async function runDirectActions(ctx, actions, { cwd } = {}) {
141
+ if (!ctx.enabled || !actions?.length) return [];
142
+ return executeActions(actions, { cwd });
143
+ }
144
+
145
+ /**
146
+ * Clear feedback queue after coder addresses it.
147
+ */
148
+ export function clearFeedback(ctx) {
149
+ if (!ctx.enabled) return;
150
+ queue.clear(ctx.feedbackQueue);
151
+ }
152
+
153
+ /**
154
+ * Get a summary of brain activity for the session.
155
+ */
156
+ export function summarize(ctx) {
157
+ if (!ctx.enabled) return null;
158
+ return {
159
+ feedbackQueueSize: ctx.feedbackQueue.entries.length,
160
+ queueCategories: queue.countByCategory(ctx.feedbackQueue),
161
+ consecutiveVerificationFailures: ctx.verificationTracker.consecutiveFailures,
162
+ compressionSaved: ctx.compressionStats.totalSaved,
163
+ compressionPerRole: ctx.compressionStats.perRole
164
+ };
165
+ }