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
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
 
@@ -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,7 +17,7 @@ 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",
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" },
@@ -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
+ }
@@ -0,0 +1,139 @@
1
+ /**
2
+ * CI/CD integration — early PR creation, incremental push, review dispatch.
3
+ * Extracted from orchestrator.js — self-contained feature, only active when ci.enabled.
4
+ */
5
+ import { saveSession } from "../session-store.js";
6
+ import { earlyPrCreation, incrementalPush } from "../git/automation.js";
7
+ import { emitProgress, makeEvent } from "../utils/events.js";
8
+
9
+ export async function tryCiComment({ config, session, logger, agent, body }) {
10
+ if (!config.ci?.enabled || !session.ci_pr_number) return;
11
+ try {
12
+ const { dispatchComment } = await import("../ci/dispatch.js");
13
+ const { detectRepo } = await import("../ci/repo.js");
14
+ const repo = await detectRepo();
15
+ if (repo) {
16
+ await dispatchComment({
17
+ repo, prNumber: session.ci_pr_number, agent,
18
+ body, ciConfig: config.ci
19
+ });
20
+ }
21
+ } catch { /* non-blocking */ }
22
+ }
23
+
24
+ function formatCommitList(commits) {
25
+ return commits.map((c) => `- \`${c.hash.slice(0, 7)}\` ${c.message}`).join("\n");
26
+ }
27
+
28
+ async function ciIncrementalPush({ config, session, gitCtx, task, logger, repo, dispatchComment }) {
29
+ const pushResult = await incrementalPush({ gitCtx, task, logger, session });
30
+ if (!pushResult) return;
31
+
32
+ const { accumulateCommit } = await import("../planning-game/pipeline-adapter.js");
33
+ for (const c of pushResult.commits) accumulateCommit(session, c);
34
+
35
+ session.ci_commits = [...(session.ci_commits ?? []), ...pushResult.commits];
36
+ await saveSession(session);
37
+
38
+ if (!repo) return;
39
+ const feedback = session.last_reviewer_feedback || "N/A";
40
+ await dispatchComment({
41
+ repo, prNumber: session.ci_pr_number, agent: "Coder",
42
+ body: `Issues fixed:\n${feedback}\n\nCommits:\n${formatCommitList(pushResult.commits)}`,
43
+ ciConfig: config.ci
44
+ });
45
+ }
46
+
47
+ async function ciCreateEarlyPr({ config, session, emitter, eventBase, gitCtx, task, logger, stageResults, i, repo, dispatchComment }) {
48
+ const earlyPr = await earlyPrCreation({ gitCtx, task, logger, session, stageResults });
49
+ if (!earlyPr) return;
50
+
51
+ const { accumulateCommit } = await import("../planning-game/pipeline-adapter.js");
52
+ for (const c of earlyPr.commits) accumulateCommit(session, c);
53
+
54
+ session.ci_pr_number = earlyPr.prNumber;
55
+ session.ci_pr_url = earlyPr.prUrl;
56
+ session.ci_commits = earlyPr.commits;
57
+ await saveSession(session);
58
+ emitProgress(emitter, makeEvent("ci:pr-created", { ...eventBase, stage: "ci" }, {
59
+ message: `Early PR created: #${earlyPr.prNumber}`,
60
+ detail: { prNumber: earlyPr.prNumber, prUrl: earlyPr.prUrl }
61
+ }));
62
+
63
+ if (!repo) return;
64
+ await dispatchComment({
65
+ repo, prNumber: earlyPr.prNumber, agent: "Coder",
66
+ body: `Iteration ${i} completed.\n\nCommits:\n${formatCommitList(earlyPr.commits)}`,
67
+ ciConfig: config.ci
68
+ });
69
+ }
70
+
71
+ export async function handleCiEarlyPrOrPush({ ciEnabled, config, session, emitter, eventBase, gitCtx, task, logger, stageResults, i }) {
72
+ if (!ciEnabled) return;
73
+
74
+ try {
75
+ const { dispatchComment } = await import("../ci/dispatch.js");
76
+ const { detectRepo } = await import("../ci/repo.js");
77
+ const repo = await detectRepo();
78
+
79
+ if (session.ci_pr_number) {
80
+ await ciIncrementalPush({ config, session, gitCtx, task, logger, repo, dispatchComment });
81
+ } else {
82
+ await ciCreateEarlyPr({ config, session, emitter, eventBase, gitCtx, task, logger, stageResults, i, repo, dispatchComment });
83
+ }
84
+ } catch (err) {
85
+ logger.warn(`CI early PR/push failed (non-blocking): ${err.message}`);
86
+ }
87
+ }
88
+
89
+ export function formatBlockingIssues(issues) {
90
+ return issues?.map((x) => `- ${x.id || "ISSUE"} [${x.severity || ""}] ${x.description}`).join("\n") || "";
91
+ }
92
+
93
+ function formatSuggestions(suggestions) {
94
+ return suggestions?.map((s) => {
95
+ const detail = typeof s === "string" ? s : `${s.id || ""} ${s.description || s}`;
96
+ return `- ${detail}`;
97
+ }).join("\n") || "";
98
+ }
99
+
100
+ function buildReviewCommentBody(review, i) {
101
+ const status = review.approved ? "APPROVED" : "REQUEST_CHANGES";
102
+ const blocking = formatBlockingIssues(review.blocking_issues);
103
+ const suggestions = formatSuggestions(review.non_blocking_suggestions);
104
+ let body = `Review iteration ${i}: ${status}`;
105
+ if (blocking) body += `\n\n**Blocking:**\n${blocking}`;
106
+ if (suggestions) body += `\n\n**Suggestions:**\n${suggestions}`;
107
+ return body;
108
+ }
109
+
110
+ export async function handleCiReviewDispatch({ ciEnabled, config, session, review, i, logger }) {
111
+ if (!ciEnabled || !session.ci_pr_number) return;
112
+
113
+ try {
114
+ const { dispatchReview, dispatchComment } = await import("../ci/dispatch.js");
115
+ const { detectRepo } = await import("../ci/repo.js");
116
+ const repo = await detectRepo();
117
+ if (!repo) return;
118
+
119
+ const bc = config.ci;
120
+ const reviewEvent = review.approved ? "APPROVE" : "REQUEST_CHANGES";
121
+ const reviewBody = review.approved
122
+ ? (review.summary || "Approved")
123
+ : (formatBlockingIssues(review.blocking_issues) || review.summary || "Changes requested");
124
+
125
+ await dispatchReview({
126
+ repo, prNumber: session.ci_pr_number,
127
+ event: reviewEvent, body: reviewBody, agent: "Reviewer", ciConfig: bc
128
+ });
129
+
130
+ await dispatchComment({
131
+ repo, prNumber: session.ci_pr_number, agent: "Reviewer",
132
+ body: buildReviewCommentBody(review, i), ciConfig: bc
133
+ });
134
+
135
+ logger.info(`CI: dispatched review for PR #${session.ci_pr_number}`);
136
+ } catch (err) {
137
+ logger.warn(`CI dispatch failed (non-blocking): ${err.message}`);
138
+ }
139
+ }