karajan-code 2.7.4 → 2.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/package.json +24 -4
- package/scripts/diet-baseline.mjs +170 -0
- package/scripts/regen-arch-stats.sh +71 -0
- package/src/agents/availability.js +15 -9
- package/src/agents/claude-agent.js +143 -45
- package/src/agents/model-registry.js +1 -1
- package/src/audit/agent-readiness.js +240 -0
- package/src/audit/basal-cost.js +92 -9
- package/src/audit/deterministic-summary.js +200 -0
- package/src/audit/osv-findings.js +162 -0
- package/src/audit/semgrep-findings.js +117 -0
- package/src/audit/sonar-findings.js +81 -0
- package/src/audit/webperf-input.js +90 -0
- package/src/bootstrap.js +26 -1
- package/src/brain/decision-tracker.js +3 -1
- package/src/brain/decisor.js +1 -1
- package/src/canvas/parse-md.js +238 -0
- package/src/canvas/schema.js +101 -0
- package/src/canvas/to-flat.js +85 -0
- package/src/canvas/validate.js +163 -0
- package/src/checks/binaries.js +3 -3
- package/src/checks/ci.js +2 -2
- package/src/checks/config-files.js +6 -6
- package/src/checks/dir-setup.js +4 -0
- package/src/checks/mcp-health.js +6 -0
- package/src/checks/ports.js +91 -29
- package/src/checks/rtk.js +1 -1
- package/src/checks/skills.js +5 -1
- package/src/checks/sonar.js +2 -2
- package/src/checks/system.js +3 -3
- package/src/checks/tokens.js +1 -1
- package/src/checks/types.js +0 -12
- package/src/cli/_shared.js +30 -0
- package/src/cli/register-meta.js +218 -0
- package/src/cli/register-pipeline.js +212 -0
- package/src/cli/register-plan.js +152 -0
- package/src/cli/register-roles-skills.js +38 -0
- package/src/cli/register-sonar.js +28 -0
- package/src/cli.js +83 -468
- package/src/commands/agents.js +18 -6
- package/src/commands/architect.js +29 -2
- package/src/commands/audit.js +265 -30
- package/src/commands/board.js +160 -30
- package/src/commands/clean.js +83 -0
- package/src/commands/code.js +7 -2
- package/src/commands/discover.js +7 -2
- package/src/commands/doctor.js +1 -1
- package/src/commands/init.js +2 -2
- package/src/commands/plan/_shared.js +40 -0
- package/src/commands/plan/add-hu.js +21 -0
- package/src/commands/plan/delete.js +9 -0
- package/src/commands/plan/generate.js +284 -0
- package/src/commands/plan/index.js +14 -0
- package/src/commands/plan/list.js +21 -0
- package/src/commands/plan/ready.js +46 -0
- package/src/commands/plan/remove-hu.js +17 -0
- package/src/commands/plan/show.js +81 -0
- package/src/commands/plan/validate.js +20 -0
- package/src/commands/plan.js +14 -291
- package/src/commands/researcher.js +7 -2
- package/src/commands/resume.js +24 -1
- package/src/commands/review.js +7 -2
- package/src/commands/run.js +52 -1
- package/src/commands/status.js +1 -1
- package/src/commands/sync.js +75 -0
- package/src/commands/triage.js +7 -2
- package/src/commands/undo.js +1 -1
- package/src/config/defaults.js +213 -0
- package/src/config/loader.js +137 -0
- package/src/config/overrides.js +199 -0
- package/src/config/role-resolver.js +159 -0
- package/src/config/test-harness.js +78 -0
- package/src/config.js +30 -632
- package/src/git/automation.js +2 -2
- package/src/guards/output-guard.js +0 -2
- package/src/hu/acceptance-runner.js +52 -10
- package/src/hu/store.js +11 -0
- package/src/mcp/handlers/direct-handlers.js +2 -5
- package/src/mcp/handlers/management-handlers.js +48 -4
- package/src/mcp/handlers/run-handler.js +9 -8
- package/src/mcp/preflight.js +32 -13
- package/src/mcp/server-handlers.js +4 -3
- package/src/mcp/suggest-handler.js +3 -2
- package/src/mcp/tools.js +15 -0
- package/src/orchestrator/agent-fallback.js +1 -1
- package/src/orchestrator/ci-integration.js +11 -11
- package/src/orchestrator/config-init.js +25 -7
- package/src/orchestrator/direct-actions.js +7 -3
- package/src/orchestrator/drivers/error-recovery.js +287 -0
- package/src/orchestrator/drivers/init-context.js +222 -0
- package/src/orchestrator/drivers/iteration-loop.js +301 -0
- package/src/orchestrator/drivers/iteration-phases/coder-and-refactorer.js +48 -0
- package/src/orchestrator/drivers/iteration-phases/guards.js +84 -0
- package/src/orchestrator/drivers/iteration-phases/handle-approved.js +29 -0
- package/src/orchestrator/drivers/iteration-phases/quality-gates.js +76 -0
- package/src/orchestrator/drivers/iteration-phases/reviewer-gate.js +60 -0
- package/src/orchestrator/drivers/post-loop.js +370 -0
- package/src/orchestrator/drivers/pre-loop-phases/auto-hu-batch.js +116 -0
- package/src/orchestrator/drivers/pre-loop-phases/config-deprecations.js +47 -0
- package/src/orchestrator/drivers/pre-loop-phases/ensure-addyosmani-skills.js +84 -0
- package/src/orchestrator/drivers/pre-loop-phases/inject-loaded-plan.js +268 -0
- package/src/orchestrator/drivers/pre-loop.js +431 -0
- package/src/orchestrator/drivers/run-hu-batch.js +367 -0
- package/src/orchestrator/feedback-queue.js +0 -1
- package/src/orchestrator/flow-control.js +4 -3
- package/src/orchestrator/flow-runner.js +121 -2077
- package/src/orchestrator/fs-leak-detector.js +156 -0
- package/src/orchestrator/hu-sub-pipeline.js +197 -9
- package/src/orchestrator/integrations.js +81 -0
- package/src/orchestrator/post-loop-stages.js +35 -8
- package/src/orchestrator/preflight-checks.js +20 -7
- package/src/orchestrator/repair/repair-detector.js +117 -0
- package/src/orchestrator/repair/repair-runner.js +142 -0
- package/src/orchestrator/reviewer-fallback.js +1 -1
- package/src/orchestrator/role-output-compressor.js +0 -1
- package/src/orchestrator/session-journal.js +4 -41
- package/src/orchestrator/solomon-escalation.js +3 -2
- package/src/orchestrator/solomon-rules.js +7 -3
- package/src/orchestrator/stages/architect-stage.js +12 -11
- package/src/orchestrator/stages/coder-stage.js +56 -7
- package/src/orchestrator/stages/hu-reviewer-phases/detect-and-apply-splits.js +127 -0
- package/src/orchestrator/stages/hu-reviewer-stage.js +6 -79
- package/src/orchestrator/stages/planner-stage.js +1 -1
- package/src/orchestrator/stages/research-stage.js +1 -1
- package/src/orchestrator/stages/reviewer-stage.js +7 -7
- package/src/orchestrator/stages/sonar-stage.js +94 -10
- package/src/orchestrator/stages/stage-classes.js +84 -0
- package/src/orchestrator/stages/stage-executor.js +1 -1
- package/src/orchestrator/stages/triage-stage.js +1 -1
- package/src/orchestrator/standby.js +3 -2
- package/src/orchestrator/verification-gate.js +20 -12
- package/src/plan/adr-generator.js +170 -0
- package/src/plan/adr-loader.js +60 -0
- package/src/plan/plan-executor.js +19 -1
- package/src/plan/plan-hu-ops.js +170 -1
- package/src/plan/plan-reviewer.js +199 -0
- package/src/plan/plan-schema.js +69 -1
- package/src/plan/plan-store.js +40 -17
- package/src/plan/spec-detector.js +61 -0
- package/src/plan/tests-synthesizer.js +116 -0
- package/src/planning-game/client.js +3 -28
- package/src/planning-game/decomposition.js +1 -1
- package/src/planning-game/pipeline-adapter.js +37 -6
- package/src/prompts/audit.js +206 -6
- package/src/prompts/coder.js +172 -1
- package/src/prompts/hu-reviewer.js +0 -1
- package/src/prompts/planner.js +81 -4
- package/src/prompts/rtk-snippet.js +1 -1
- package/src/review/diff-generator.js +35 -22
- package/src/review/snapshot-diff.js +1 -1
- package/src/roles/audit-role.js +125 -13
- package/src/roles/coder-role.js +25 -3
- package/src/roles/planner-role.js +42 -1
- package/src/roles/repairer-role.js +181 -0
- package/src/roles/tester-role.js +70 -4
- package/src/session/mutators.js +304 -0
- package/src/session/runtime-overrides.js +59 -0
- package/src/{session-store.js → session/store.js} +2 -2
- package/src/skills/semantic-detector.js +10 -5
- package/src/skills/skill-detector.js +1 -1
- package/src/sonar/api.js +1 -1
- package/src/sonar/cloud-scanner.js +0 -1
- package/src/sonar/credentials.js +14 -6
- package/src/sonar/discovery.js +123 -0
- package/src/sonar/manager.js +18 -0
- package/src/sync/detect-drift.js +113 -0
- package/src/tracker-bootstrap.js +44 -0
- package/src/utils/agent-detect.js +15 -11
- package/src/utils/board-prompt-bridge.js +109 -0
- package/src/utils/cli-progress.js +161 -0
- package/src/utils/cwd-confirm.js +39 -0
- package/src/utils/derive-project-name-from-cwd.js +119 -0
- package/src/utils/display/event-handlers.js +1 -1
- package/src/utils/display/header.js +1 -1
- package/src/utils/display/session-summary.js +3 -3
- package/src/utils/display/solomon.js +1 -1
- package/src/utils/garbage-collector.js +396 -0
- package/src/utils/git.js +16 -4
- package/src/utils/locale.js +1 -1
- package/src/utils/paths.js +21 -1
- package/src/utils/process.js +1 -1
- package/src/utils/prompt-project-name.js +35 -0
- package/src/utils/role-assigner.js +1 -1
- package/src/utils/run-context.js +75 -0
- package/src/utils/run-log.js +12 -0
- package/src/utils/task-file.js +1 -1
- package/src/utils/telemetry.js +13 -1
- package/src/webperf/devtools-detect.js +1 -1
- package/templates/coder-rules.md +13 -0
- package/templates/roles/coder.md +34 -0
- package/templates/roles/repairer.md +94 -0
package/src/config.js
CHANGED
|
@@ -1,636 +1,34 @@
|
|
|
1
|
-
import fs from "node:fs/promises";
|
|
2
|
-
import path from "node:path";
|
|
3
|
-
import yaml from "js-yaml";
|
|
4
|
-
import { ensureDir, exists } from "./utils/fs.js";
|
|
5
|
-
import { getKarajanHome } from "./utils/paths.js";
|
|
6
|
-
import { safeParseConfig, formatConfigIssues } from "./config/schema.js";
|
|
7
|
-
|
|
8
1
|
/**
|
|
2
|
+
* Karajan config barrel — public re-exports for back-compat.
|
|
3
|
+
*
|
|
4
|
+
* Pre-v2.7.5 this file was 636 LOC with fan-in 28: it bundled DEFAULTS
|
|
5
|
+
* data, loadConfig/writeConfig I/O, applyRunOverrides merge logic,
|
|
6
|
+
* resolveRole lookup and validateConfig schema gate — touching any of
|
|
7
|
+
* those risked rippling to a quarter of the codebase. TSK-0332 (Oleada 2
|
|
8
|
+
* of the audit refactor) split along the natural seams:
|
|
9
|
+
*
|
|
10
|
+
* - `src/config/defaults.js` — pure data, DEFAULTS
|
|
11
|
+
* - `src/config/loader.js` — fs/yaml I/O (getConfigPath,
|
|
12
|
+
* loadConfig, writeConfig, etc.)
|
|
13
|
+
* - `src/config/overrides.js` — CLI-flag merge (applyRunOverrides)
|
|
14
|
+
* - `src/config/role-resolver.js` — provider/model/validate
|
|
15
|
+
* - `src/config/schema.js` — Valibot schema (pre-existing)
|
|
16
|
+
*
|
|
17
|
+
* This file is now a thin barrel so the 28+ consumers importing from
|
|
18
|
+
* `src/config.js` keep working unchanged. New code should import from
|
|
19
|
+
* the specific submodule.
|
|
20
|
+
*
|
|
9
21
|
* @typedef {import("./config/schema.js").KarajanConfig} KarajanConfig
|
|
10
22
|
*/
|
|
11
23
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
security: { provider: null, model: null },
|
|
24
|
-
impeccable: { provider: null, model: null },
|
|
25
|
-
triage: { provider: null, model: null },
|
|
26
|
-
discover: { provider: null, model: null },
|
|
27
|
-
architect: { provider: null, model: null },
|
|
28
|
-
hu_reviewer: { provider: null, model: null }
|
|
29
|
-
},
|
|
30
|
-
pipeline: {
|
|
31
|
-
planner: { enabled: false },
|
|
32
|
-
refactorer: { enabled: false },
|
|
33
|
-
solomon: { enabled: true },
|
|
34
|
-
researcher: { enabled: false },
|
|
35
|
-
tester: { enabled: true },
|
|
36
|
-
security: { enabled: true },
|
|
37
|
-
impeccable: { enabled: false },
|
|
38
|
-
triage: { enabled: true },
|
|
39
|
-
discover: { enabled: false },
|
|
40
|
-
architect: { enabled: false },
|
|
41
|
-
hu_reviewer: { enabled: false },
|
|
42
|
-
brain: { enabled: true },
|
|
43
|
-
auto_simplify: true
|
|
44
|
-
},
|
|
45
|
-
brain: {
|
|
46
|
-
enabled: true,
|
|
47
|
-
provider: "claude",
|
|
48
|
-
bypass_solomon_on_correctness: true,
|
|
49
|
-
max_consecutive_verification_failures: 2
|
|
50
|
-
},
|
|
51
|
-
review_mode: "standard",
|
|
52
|
-
max_iterations: 5,
|
|
53
|
-
hu_max_iterations: 3,
|
|
54
|
-
max_budget_usd: null,
|
|
55
|
-
review_rules: "./.karajan/review-rules.md",
|
|
56
|
-
coder_rules: "./.karajan/coder-rules.md",
|
|
57
|
-
base_branch: "main",
|
|
58
|
-
coder_options: { model: null, auto_approve: true, fallback_coder: null },
|
|
59
|
-
reviewer_options: {
|
|
60
|
-
output_format: "json",
|
|
61
|
-
require_schema: true,
|
|
62
|
-
model: null,
|
|
63
|
-
deterministic: true,
|
|
64
|
-
retries: 1,
|
|
65
|
-
fallback_reviewer: null
|
|
66
|
-
},
|
|
67
|
-
development: {
|
|
68
|
-
methodology: "tdd",
|
|
69
|
-
require_test_changes: true,
|
|
70
|
-
test_file_patterns: ["/tests/", "/__tests__/", ".test.", ".spec."],
|
|
71
|
-
source_file_extensions: [".js", ".jsx", ".ts", ".tsx", ".py", ".go", ".java", ".rb", ".php", ".cs"]
|
|
72
|
-
},
|
|
73
|
-
sonarqube: {
|
|
74
|
-
enabled: true,
|
|
75
|
-
host: "http://localhost:9000",
|
|
76
|
-
external: false,
|
|
77
|
-
container_name: "karajan-sonarqube",
|
|
78
|
-
network: "karajan_sonar_net",
|
|
79
|
-
volumes: {
|
|
80
|
-
data: "karajan_sonar_data",
|
|
81
|
-
logs: "karajan_sonar_logs",
|
|
82
|
-
extensions: "karajan_sonar_extensions"
|
|
83
|
-
},
|
|
84
|
-
timeouts: {
|
|
85
|
-
healthcheck_seconds: 5,
|
|
86
|
-
compose_up_ms: 300000,
|
|
87
|
-
compose_control_ms: 120000,
|
|
88
|
-
logs_ms: 30000,
|
|
89
|
-
scanner_ms: 900000
|
|
90
|
-
},
|
|
91
|
-
token: null,
|
|
92
|
-
project_key: null,
|
|
93
|
-
admin_user: null,
|
|
94
|
-
admin_password: null,
|
|
95
|
-
coverage: {
|
|
96
|
-
enabled: false,
|
|
97
|
-
command: null,
|
|
98
|
-
timeout_ms: 300000,
|
|
99
|
-
block_on_failure: true,
|
|
100
|
-
lcov_report_path: null
|
|
101
|
-
},
|
|
102
|
-
quality_gate: true,
|
|
103
|
-
enforcement_profile: "pragmatic",
|
|
104
|
-
gate_block_on: [
|
|
105
|
-
"new_reliability_rating=E",
|
|
106
|
-
"new_security_rating=E",
|
|
107
|
-
"new_maintainability_rating=E",
|
|
108
|
-
"new_coverage<80",
|
|
109
|
-
"new_duplicated_lines_density>5"
|
|
110
|
-
],
|
|
111
|
-
fail_on: ["BLOCKER", "CRITICAL"],
|
|
112
|
-
ignore_on: ["INFO"],
|
|
113
|
-
max_scan_retries: 3,
|
|
114
|
-
scanner: {
|
|
115
|
-
sources: "src,public,lib",
|
|
116
|
-
exclusions: "**/node_modules/**,**/fake-apps/**,**/scripts/**,**/playground/**,**/dist/**,**/build/**,**/*.min.js",
|
|
117
|
-
test_inclusions: "**/*.test.js,**/*.spec.js,**/tests/**,**/__tests__/**",
|
|
118
|
-
coverage_exclusions: "**/tests/**,**/__tests__/**,**/*.test.js,**/*.spec.js",
|
|
119
|
-
disabled_rules: ["javascript:S1116", "javascript:S3776"]
|
|
120
|
-
}
|
|
121
|
-
},
|
|
122
|
-
sonarcloud: {
|
|
123
|
-
enabled: false,
|
|
124
|
-
organization: null,
|
|
125
|
-
token: null,
|
|
126
|
-
project_key: null,
|
|
127
|
-
host: "https://sonarcloud.io",
|
|
128
|
-
scanner: {
|
|
129
|
-
sources: "src,public,lib",
|
|
130
|
-
exclusions: "**/node_modules/**,**/dist/**,**/build/**,**/*.min.js",
|
|
131
|
-
test_inclusions: "**/*.test.js,**/*.spec.js,**/tests/**,**/__tests__/**"
|
|
132
|
-
}
|
|
133
|
-
},
|
|
134
|
-
hu_board: {
|
|
135
|
-
enabled: false,
|
|
136
|
-
port: 4000,
|
|
137
|
-
auto_start: false
|
|
138
|
-
},
|
|
139
|
-
language: "en",
|
|
140
|
-
hu_language: "en",
|
|
141
|
-
policies: {},
|
|
142
|
-
serena: { enabled: false },
|
|
143
|
-
planning_game: { enabled: false, project_id: null, codeveloper: null },
|
|
144
|
-
skills: {
|
|
145
|
-
mode: "auto",
|
|
146
|
-
sources: ["addyosmani", "openskills", "local"],
|
|
147
|
-
addyosmani: {
|
|
148
|
-
enabled: true,
|
|
149
|
-
refreshDays: 7,
|
|
150
|
-
repoUrl: "https://github.com/addyosmani/agent-skills.git"
|
|
151
|
-
}
|
|
152
|
-
},
|
|
153
|
-
ci: { enabled: false, review_event: "kj-review", comment_event: "kj-comment", comment_prefix: true },
|
|
154
|
-
git: { auto_commit: false, auto_push: false, auto_pr: false, auto_rebase: true, branch_prefix: "feat/" },
|
|
155
|
-
output: { report_dir: "./.reviews", log_level: "info", quiet: true },
|
|
156
|
-
budget: {
|
|
157
|
-
warn_threshold_pct: 80,
|
|
158
|
-
currency: "usd",
|
|
159
|
-
exchange_rate_eur: 0.92
|
|
160
|
-
},
|
|
161
|
-
model_selection: {
|
|
162
|
-
enabled: true,
|
|
163
|
-
tiers: {},
|
|
164
|
-
role_overrides: {}
|
|
165
|
-
},
|
|
166
|
-
session: {
|
|
167
|
-
max_iteration_minutes: 30,
|
|
168
|
-
max_total_minutes: 120,
|
|
169
|
-
max_planner_minutes: 60,
|
|
170
|
-
checkpoint_interval_minutes: 5,
|
|
171
|
-
max_agent_silence_minutes: 20,
|
|
172
|
-
fail_fast_repeats: 2,
|
|
173
|
-
repeat_detection_threshold: 2,
|
|
174
|
-
max_sonar_retries: 3,
|
|
175
|
-
max_reviewer_retries: 3,
|
|
176
|
-
max_tester_retries: 1,
|
|
177
|
-
max_security_retries: 1,
|
|
178
|
-
max_auto_resumes: 2,
|
|
179
|
-
expiry_days: 30
|
|
180
|
-
},
|
|
181
|
-
failFast: {
|
|
182
|
-
repeatThreshold: 2
|
|
183
|
-
},
|
|
184
|
-
webperf: {
|
|
185
|
-
enabled: true,
|
|
186
|
-
devtools_mcp: false,
|
|
187
|
-
thresholds: { lcp: 2500, cls: 0.1, inp: 200 }
|
|
188
|
-
},
|
|
189
|
-
telemetry: true,
|
|
190
|
-
guards: {
|
|
191
|
-
output: {
|
|
192
|
-
enabled: true,
|
|
193
|
-
patterns: [],
|
|
194
|
-
protected_files: [],
|
|
195
|
-
on_violation: "block"
|
|
196
|
-
},
|
|
197
|
-
perf: {
|
|
198
|
-
enabled: true,
|
|
199
|
-
patterns: [],
|
|
200
|
-
block_on_warning: false,
|
|
201
|
-
frontend_extensions: []
|
|
202
|
-
},
|
|
203
|
-
intent: {
|
|
204
|
-
enabled: false,
|
|
205
|
-
patterns: [],
|
|
206
|
-
confidence_threshold: 0.85
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
};
|
|
210
|
-
|
|
211
|
-
function mergeDeep(base, override) {
|
|
212
|
-
const output = { ...base };
|
|
213
|
-
for (const [key, value] of Object.entries(override || {})) {
|
|
214
|
-
if (Array.isArray(value)) {
|
|
215
|
-
output[key] = value;
|
|
216
|
-
} else if (value && typeof value === "object") {
|
|
217
|
-
output[key] = mergeDeep(base[key] || {}, value);
|
|
218
|
-
} else {
|
|
219
|
-
output[key] = value;
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
return output;
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
export function getConfigPath() {
|
|
226
|
-
return path.join(getKarajanHome(), "kj.config.yml");
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
export function getProjectConfigPath(projectDir = process.cwd()) {
|
|
230
|
-
return path.join(projectDir, ".karajan", "kj.config.yml");
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
export async function loadProjectConfig(projectDir = process.cwd()) {
|
|
234
|
-
const projectConfigPath = getProjectConfigPath(projectDir);
|
|
235
|
-
if (!(await exists(projectConfigPath))) {
|
|
236
|
-
return null;
|
|
237
|
-
}
|
|
238
|
-
const raw = await fs.readFile(projectConfigPath, "utf8");
|
|
239
|
-
return yaml.load(raw, { json: true }) || {};
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
async function loadProjectPricingOverrides(projectDir = process.cwd()) {
|
|
243
|
-
const projectConfigPath = path.join(projectDir, ".karajan.yml");
|
|
244
|
-
if (!(await exists(projectConfigPath))) {
|
|
245
|
-
return null;
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
const raw = await fs.readFile(projectConfigPath, "utf8");
|
|
249
|
-
const parsed = yaml.load(raw, { json: true }) || {};
|
|
250
|
-
const pricing = parsed?.budget?.pricing;
|
|
251
|
-
if (!pricing || typeof pricing !== "object") {
|
|
252
|
-
return null;
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
return pricing;
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
export async function loadConfig(projectDir) {
|
|
259
|
-
const configPath = getConfigPath();
|
|
260
|
-
const projectPricing = await loadProjectPricingOverrides(projectDir);
|
|
261
|
-
|
|
262
|
-
// Load global config
|
|
263
|
-
let globalConfig = {};
|
|
264
|
-
const globalExists = await exists(configPath);
|
|
265
|
-
if (globalExists) {
|
|
266
|
-
const raw = await fs.readFile(configPath, "utf8");
|
|
267
|
-
globalConfig = yaml.load(raw, { json: true }) || {};
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
// Load project config (.karajan/kj.config.yml)
|
|
271
|
-
const projectConfig = await loadProjectConfig(projectDir);
|
|
272
|
-
|
|
273
|
-
// Merge: DEFAULTS < global < project
|
|
274
|
-
let merged = mergeDeep(DEFAULTS, globalConfig);
|
|
275
|
-
if (projectConfig) {
|
|
276
|
-
merged = mergeDeep(merged, projectConfig);
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
if (projectPricing) {
|
|
280
|
-
merged.budget = mergeDeep(merged.budget || {}, { pricing: projectPricing });
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
// KJC-TSK-0318 — schema-validate the merged config so invalid values
|
|
284
|
-
// (review_mode typos, max_iterations=0, out-of-range ports) surface at
|
|
285
|
-
// load time with a clear error instead of crashing deep inside the
|
|
286
|
-
// orchestrator. safeParse + explicit error prefix keeps the message
|
|
287
|
-
// actionable.
|
|
288
|
-
const validation = safeParseConfig(merged);
|
|
289
|
-
if (!validation.success) {
|
|
290
|
-
throw new Error(`Invalid Karajan config:\n${formatConfigIssues(validation.issues)}`);
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
// Deprecation tracking — `sonarqube.enabled` was a footgun: users would
|
|
294
|
-
// set it to `false` in their global ~/.karajan/kj.config.yml and then
|
|
295
|
-
// wonder why Sonar didn't run on a code task. Since v2.7.4 Sonar is
|
|
296
|
-
// intrinsic to Karajan for code tasks; the field is ignored. We surface
|
|
297
|
-
// a warning at run start (see emitConfigDeprecations in flow-runner.js)
|
|
298
|
-
// so users notice and clean up their config. Don't throw — keep
|
|
299
|
-
// back-compat so existing scripts keep working.
|
|
300
|
-
const userSetSonarEnabled = (globalConfig?.sonarqube?.enabled !== undefined)
|
|
301
|
-
|| (projectConfig?.sonarqube?.enabled !== undefined);
|
|
302
|
-
if (userSetSonarEnabled) {
|
|
303
|
-
merged._deprecated = merged._deprecated || {};
|
|
304
|
-
merged._deprecated.sonarqubeEnabledKey = true;
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
return { config: merged, path: configPath, exists: globalExists, hasProjectConfig: !!projectConfig };
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
export async function writeConfig(configPath, config) {
|
|
311
|
-
await ensureDir(path.dirname(configPath));
|
|
312
|
-
await fs.writeFile(configPath, yaml.dump(config, { lineWidth: 120 }), "utf8");
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
// Declarative mappings for applyRunOverrides to reduce cognitive complexity.
|
|
316
|
-
|
|
317
|
-
// Role provider flags: [flagName, roleName] — truthy check
|
|
318
|
-
const ROLE_PROVIDER_FLAGS = [
|
|
319
|
-
["planner", "planner"], ["coder", "coder"], ["reviewer", "reviewer"],
|
|
320
|
-
["refactorer", "refactorer"], ["solomon", "solomon"], ["researcher", "researcher"],
|
|
321
|
-
["tester", "tester"], ["security", "security"], ["triage", "triage"],
|
|
322
|
-
["discover", "discover"], ["architect", "architect"]
|
|
323
|
-
];
|
|
324
|
-
|
|
325
|
-
// Role model flags: [flagName, roleName] — truthy check, String coercion
|
|
326
|
-
const ROLE_MODEL_FLAGS = [
|
|
327
|
-
["plannerModel", "planner"], ["coderModel", "coder"], ["reviewerModel", "reviewer"],
|
|
328
|
-
["refactorerModel", "refactorer"], ["solomonModel", "solomon"], ["discoverModel", "discover"],
|
|
329
|
-
["architectModel", "architect"]
|
|
330
|
-
];
|
|
331
|
-
|
|
332
|
-
// Pipeline enable flags: [flagName, pipelineKey] — !== undefined check, Boolean coercion
|
|
333
|
-
const PIPELINE_ENABLE_FLAGS = [
|
|
334
|
-
["enablePlanner", "planner"], ["enableRefactorer", "refactorer"],
|
|
335
|
-
["enableSolomon", "solomon"], ["enableResearcher", "researcher"],
|
|
336
|
-
["enableTester", "tester"], ["enableSecurity", "security"], ["enableImpeccable", "impeccable"],
|
|
337
|
-
["enableTriage", "triage"], ["enableDiscover", "discover"],
|
|
338
|
-
["enableArchitect", "architect"],
|
|
339
|
-
["enableHuReviewer", "hu_reviewer"]
|
|
340
|
-
];
|
|
341
|
-
|
|
342
|
-
const AUTO_SIMPLIFY_FLAG = "autoSimplify";
|
|
343
|
-
|
|
344
|
-
// Scalar flags: [flagName, setter] — truthy check.
|
|
345
|
-
// Note: max_iterations was previously here but `0` was silently ignored
|
|
346
|
-
// (truthy check skipped it). It now lives in UNDEF_CHECK_FLAGS so
|
|
347
|
-
// `--max-iterations 0` reaches the schema validator and errors cleanly
|
|
348
|
-
// instead of falling through to the default (KJC-TSK-0318).
|
|
349
|
-
const SCALAR_FLAGS = [
|
|
350
|
-
["mode", (out, v) => { out.review_mode = v; }],
|
|
351
|
-
["maxIterationMinutes", (out, v) => { out.session.max_iteration_minutes = Number(v); }],
|
|
352
|
-
["maxTotalMinutes", (out, v) => { out.session.max_total_minutes = Number(v); }],
|
|
353
|
-
["checkpointInterval", (out, v) => { out.session.checkpoint_interval_minutes = Number(v); }],
|
|
354
|
-
["baseBranch", (out, v) => { out.base_branch = v; }],
|
|
355
|
-
["coderFallback", (out, v) => { out.coder_options.fallback_coder = v; }],
|
|
356
|
-
["reviewerFallback", (out, v) => { out.reviewer_options.fallback_reviewer = v; }],
|
|
357
|
-
["taskType", (out, v) => { out.taskType = String(v); }],
|
|
358
|
-
["branchPrefix", (out, v) => { out.git.branch_prefix = String(v); }]
|
|
359
|
-
];
|
|
360
|
-
|
|
361
|
-
// Boolean/undefined-check flags: [flagName, setter] — !== undefined check.
|
|
362
|
-
// These preserve falsy values (false, 0) so --no-rebase correctly sets
|
|
363
|
-
// auto_rebase=false and --reviewer-retries 0 correctly disables retries.
|
|
364
|
-
const UNDEF_CHECK_FLAGS = [
|
|
365
|
-
["maxIterations", (out, v) => { out.max_iterations = Number(v); }],
|
|
366
|
-
["reviewerRetries", (out, v) => { out.reviewer_options.retries = Number(v); }],
|
|
367
|
-
["autoCommit", (out, v) => { out.git.auto_commit = Boolean(v); }],
|
|
368
|
-
["autoPush", (out, v) => { out.git.auto_push = Boolean(v); }],
|
|
369
|
-
["autoPr", (out, v) => { out.git.auto_pr = Boolean(v); }],
|
|
370
|
-
["autoRebase", (out, v) => { out.git.auto_rebase = Boolean(v); }],
|
|
371
|
-
["enableSerena", (out, v) => { out.serena.enabled = Boolean(v); }]
|
|
372
|
-
];
|
|
373
|
-
|
|
374
|
-
function applyRoleOverrides(out, flags) {
|
|
375
|
-
for (const [flag, role] of ROLE_PROVIDER_FLAGS) {
|
|
376
|
-
if (flags[flag]) out.roles[role].provider = flags[flag];
|
|
377
|
-
}
|
|
378
|
-
// coder/reviewer also update top-level aliases
|
|
379
|
-
if (flags.coder) out.coder = flags.coder;
|
|
380
|
-
if (flags.reviewer) out.reviewer = flags.reviewer;
|
|
381
|
-
|
|
382
|
-
for (const [flag, role] of ROLE_MODEL_FLAGS) {
|
|
383
|
-
if (flags[flag]) out.roles[role].model = String(flags[flag]);
|
|
384
|
-
}
|
|
385
|
-
// reviewerModel also updates reviewer_options
|
|
386
|
-
if (flags.reviewerModel) out.reviewer_options.model = String(flags.reviewerModel);
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
function applyPipelineOverrides(out, flags) {
|
|
390
|
-
for (const [flag, key] of PIPELINE_ENABLE_FLAGS) {
|
|
391
|
-
if (flags[flag] !== undefined) out.pipeline[key].enabled = Boolean(flags[flag]);
|
|
392
|
-
}
|
|
393
|
-
if (flags.enableReviewer !== undefined) {
|
|
394
|
-
out.pipeline.reviewer = out.pipeline.reviewer || {};
|
|
395
|
-
out.pipeline.reviewer.enabled = Boolean(flags.enableReviewer);
|
|
396
|
-
}
|
|
397
|
-
}
|
|
398
|
-
|
|
399
|
-
function applyScalarAndBooleanOverrides(out, flags) {
|
|
400
|
-
for (const [flag, setter] of SCALAR_FLAGS) {
|
|
401
|
-
if (flags[flag]) setter(out, flags[flag]);
|
|
402
|
-
}
|
|
403
|
-
for (const [flag, setter] of UNDEF_CHECK_FLAGS) {
|
|
404
|
-
if (flags[flag] !== undefined) setter(out, flags[flag]);
|
|
405
|
-
}
|
|
406
|
-
}
|
|
407
|
-
|
|
408
|
-
function applyMethodologyOverride(out, flags) {
|
|
409
|
-
if (!flags.methodology) return;
|
|
410
|
-
const methodology = String(flags.methodology).toLowerCase();
|
|
411
|
-
out.development.methodology = methodology;
|
|
412
|
-
out.development.require_test_changes = methodology === "tdd";
|
|
413
|
-
}
|
|
414
|
-
|
|
415
|
-
function applyCiOverride(out, flags) {
|
|
416
|
-
out.ci = out.ci || { enabled: false };
|
|
417
|
-
if (flags.enableCi === undefined) return;
|
|
418
|
-
out.ci.enabled = Boolean(flags.enableCi);
|
|
419
|
-
// CI requires git automation (commit + push + PR)
|
|
420
|
-
if (out.ci.enabled) {
|
|
421
|
-
out.git.auto_commit = true;
|
|
422
|
-
out.git.auto_push = true;
|
|
423
|
-
out.git.auto_pr = true;
|
|
424
|
-
}
|
|
425
|
-
}
|
|
426
|
-
|
|
427
|
-
function applyOutputModeOverrides(out, flags) {
|
|
428
|
-
out.output = out.output || {};
|
|
429
|
-
// --verbose explicitly overrides quiet
|
|
430
|
-
if (flags.verbose === true) {
|
|
431
|
-
out.output.quiet = false;
|
|
432
|
-
} else if (flags.quiet === true) {
|
|
433
|
-
out.output.quiet = true;
|
|
434
|
-
}
|
|
435
|
-
// quiet defaults to true (set in DEFAULTS)
|
|
436
|
-
}
|
|
437
|
-
|
|
438
|
-
function applyMiscOverrides(out, flags) {
|
|
439
|
-
if (flags[AUTO_SIMPLIFY_FLAG] !== undefined) out.pipeline.auto_simplify = Boolean(flags[AUTO_SIMPLIFY_FLAG]);
|
|
440
|
-
// `--no-sonar` and `sonarqube.enabled: false` are deprecated since v2.7.4.
|
|
441
|
-
// Sonar is intrinsic to Karajan for code tasks (sw/refactor/add-tests) and
|
|
442
|
-
// skipped for non-code tasks (audit/doc/infra/analysis/no-code) by policy.
|
|
443
|
-
// The flag is still accepted to avoid breaking scripts; it now emits a
|
|
444
|
-
// warning at run start and is otherwise ignored. See preflight-checks.js
|
|
445
|
-
// and flow-runner.js for the actual gate (resolvedPolicies.sonar).
|
|
446
|
-
if (flags.noSonar || flags.sonar === false) {
|
|
447
|
-
out._deprecated = out._deprecated || {};
|
|
448
|
-
out._deprecated.noSonarFlag = true;
|
|
449
|
-
}
|
|
450
|
-
out.sonarcloud = out.sonarcloud || {};
|
|
451
|
-
if (flags.enableSonarcloud === true) out.sonarcloud.enabled = true;
|
|
452
|
-
if (flags.noSonarcloud === true || flags.sonarcloud === false) out.sonarcloud.enabled = false;
|
|
453
|
-
|
|
454
|
-
out.planning_game = out.planning_game || {};
|
|
455
|
-
if (flags.pgTask) out.planning_game.enabled = true;
|
|
456
|
-
if (flags.pgProject) out.planning_game.project_id = flags.pgProject;
|
|
457
|
-
|
|
458
|
-
out.model_selection = out.model_selection || { enabled: true, tiers: {}, role_overrides: {} };
|
|
459
|
-
if (flags.smartModels === true) out.model_selection.enabled = true;
|
|
460
|
-
if (flags.smartModels === false || flags.noSmartModels === true) out.model_selection.enabled = false;
|
|
461
|
-
}
|
|
462
|
-
|
|
463
|
-
export function applyRunOverrides(config, flags) {
|
|
464
|
-
const out = mergeDeep(config, {});
|
|
465
|
-
out.coder_options = out.coder_options || {};
|
|
466
|
-
out.reviewer_options = out.reviewer_options || {};
|
|
467
|
-
out.session = out.session || {};
|
|
468
|
-
out.git = out.git || {};
|
|
469
|
-
out.development = out.development || {};
|
|
470
|
-
out.sonarqube = out.sonarqube || {};
|
|
471
|
-
if (out.max_budget_usd === undefined || out.max_budget_usd === null) {
|
|
472
|
-
out.max_budget_usd = out.session.max_budget_usd ?? null;
|
|
473
|
-
}
|
|
474
|
-
out.budget = mergeDeep(DEFAULTS.budget, out.budget || {});
|
|
475
|
-
out.roles = mergeDeep(DEFAULTS.roles, out.roles || {});
|
|
476
|
-
out.pipeline = mergeDeep(DEFAULTS.pipeline, out.pipeline || {});
|
|
477
|
-
out.serena = out.serena || { enabled: false };
|
|
478
|
-
|
|
479
|
-
applyRoleOverrides(out, flags);
|
|
480
|
-
applyPipelineOverrides(out, flags);
|
|
481
|
-
applyScalarAndBooleanOverrides(out, flags);
|
|
482
|
-
applyMethodologyOverride(out, flags);
|
|
483
|
-
applyCiOverride(out, flags);
|
|
484
|
-
applyMiscOverrides(out, flags);
|
|
485
|
-
applyOutputModeOverrides(out, flags);
|
|
486
|
-
|
|
487
|
-
// KJC-TSK-0318 — re-run schema validation after overrides so CLI flags
|
|
488
|
-
// that would produce an invalid config (e.g. --max-iterations 0) fail
|
|
489
|
-
// fast with the same clear message loadConfig would have produced.
|
|
490
|
-
const validation = safeParseConfig(out);
|
|
491
|
-
if (!validation.success) {
|
|
492
|
-
throw new Error(`Invalid Karajan config after CLI overrides:\n${formatConfigIssues(validation.issues)}`);
|
|
493
|
-
}
|
|
494
|
-
|
|
495
|
-
return out;
|
|
496
|
-
}
|
|
497
|
-
|
|
498
|
-
/**
|
|
499
|
-
* Check if a model string is compatible with an agent provider.
|
|
500
|
-
* Only returns false when the model clearly belongs to a DIFFERENT provider.
|
|
501
|
-
* Returns true if we can't determine or if the model is ambiguous.
|
|
502
|
-
*/
|
|
503
|
-
const AGENT_MODEL_SIGNATURES = {
|
|
504
|
-
claude: ["claude", "sonnet", "opus", "haiku"],
|
|
505
|
-
codex: ["o4-", "o3-", "gpt-", "codex"],
|
|
506
|
-
gemini: ["gemini", "flash-"]
|
|
507
|
-
};
|
|
508
|
-
|
|
509
|
-
function isModelCompatible(agent, model) {
|
|
510
|
-
if (!model || !agent) return true;
|
|
511
|
-
const lower = model.toLowerCase();
|
|
512
|
-
|
|
513
|
-
// Check if model clearly belongs to a different provider
|
|
514
|
-
for (const [provider, signatures] of Object.entries(AGENT_MODEL_SIGNATURES)) {
|
|
515
|
-
if (provider === agent) continue;
|
|
516
|
-
if (signatures.some(s => lower.includes(s))) {
|
|
517
|
-
// Model belongs to a different provider — incompatible
|
|
518
|
-
return false;
|
|
519
|
-
}
|
|
520
|
-
}
|
|
521
|
-
|
|
522
|
-
// Model doesn't clearly belong to any other provider — allow it
|
|
523
|
-
return true;
|
|
524
|
-
}
|
|
525
|
-
|
|
526
|
-
// Roles that inherit provider/model from the coder when not explicitly configured
|
|
527
|
-
const CODER_INHERITED_ROLES = new Set([
|
|
528
|
-
"planner", "refactorer", "solomon", "researcher", "tester", "security",
|
|
529
|
-
"impeccable", "triage", "discover", "architect", "audit", "hu_reviewer", "hu-reviewer"
|
|
530
|
-
]);
|
|
531
|
-
|
|
532
|
-
function resolveProvider(roleConfig, role, roles, legacyCoder, legacyReviewer) {
|
|
533
|
-
if (roleConfig.provider) return roleConfig.provider;
|
|
534
|
-
|
|
535
|
-
// If model has "provider/model" format (e.g. "gemini/pro"), extract the provider
|
|
536
|
-
if (roleConfig.model && roleConfig.model.includes("/")) {
|
|
537
|
-
const inferredProvider = roleConfig.model.split("/")[0].toLowerCase();
|
|
538
|
-
if (AGENT_MODEL_SIGNATURES[inferredProvider]) return inferredProvider;
|
|
539
|
-
}
|
|
540
|
-
|
|
541
|
-
if (role === "coder") return legacyCoder;
|
|
542
|
-
if (role === "reviewer") return legacyReviewer;
|
|
543
|
-
if (CODER_INHERITED_ROLES.has(role)) return roles.coder?.provider || legacyCoder;
|
|
544
|
-
return null;
|
|
545
|
-
}
|
|
546
|
-
|
|
547
|
-
function resolveModel(roleConfig, role, config) {
|
|
548
|
-
if (roleConfig.model) {
|
|
549
|
-
// Strip "provider/" prefix from models like "gemini/pro" → "pro"
|
|
550
|
-
const model = roleConfig.model.includes("/")
|
|
551
|
-
? roleConfig.model.split("/").slice(1).join("/")
|
|
552
|
-
: roleConfig.model;
|
|
553
|
-
return { model, inherited: false };
|
|
554
|
-
}
|
|
555
|
-
if (role === "coder") return { model: config?.coder_options?.model ?? null, inherited: false };
|
|
556
|
-
if (role === "reviewer") return { model: config?.reviewer_options?.model ?? null, inherited: false };
|
|
557
|
-
if (CODER_INHERITED_ROLES.has(role)) {
|
|
558
|
-
const model = config?.coder_options?.model ?? null;
|
|
559
|
-
return { model, inherited: !!model };
|
|
560
|
-
}
|
|
561
|
-
return { model: null, inherited: false };
|
|
562
|
-
}
|
|
563
|
-
|
|
564
|
-
export function resolveRole(config, role) {
|
|
565
|
-
const roles = config?.roles || {};
|
|
566
|
-
const roleConfig = roles[role] || {};
|
|
567
|
-
const legacyCoder = config?.coder || null;
|
|
568
|
-
const legacyReviewer = config?.reviewer || null;
|
|
569
|
-
|
|
570
|
-
const provider = resolveProvider(roleConfig, role, roles, legacyCoder, legacyReviewer);
|
|
571
|
-
let { model, inherited: modelIsInherited } = resolveModel(roleConfig, role, config);
|
|
572
|
-
|
|
573
|
-
// Drop model if incompatible with the resolved provider (inherited or explicit)
|
|
574
|
-
if (provider && model && !isModelCompatible(provider, model)) {
|
|
575
|
-
model = null;
|
|
576
|
-
}
|
|
577
|
-
|
|
578
|
-
return { provider, model };
|
|
579
|
-
}
|
|
580
|
-
|
|
581
|
-
// Pipeline roles checked when commandName is "run": [pipelineKey, roleName]
|
|
582
|
-
const RUN_PIPELINE_ROLES = [
|
|
583
|
-
["reviewer", "reviewer"], ["triage", "triage"], ["planner", "planner"],
|
|
584
|
-
["refactorer", "refactorer"], ["researcher", "researcher"],
|
|
585
|
-
["tester", "tester"], ["security", "security"], ["impeccable", "impeccable"]
|
|
586
|
-
];
|
|
587
|
-
|
|
588
|
-
// Direct command-to-role mapping for non-"run" commands
|
|
589
|
-
const COMMAND_ROLE_MAP = {
|
|
590
|
-
discover: ["discover"],
|
|
591
|
-
plan: ["planner"],
|
|
592
|
-
code: ["coder"],
|
|
593
|
-
review: ["reviewer"]
|
|
594
|
-
};
|
|
595
|
-
|
|
596
|
-
function requiredRolesFor(commandName, config) {
|
|
597
|
-
if (commandName !== "run") {
|
|
598
|
-
return COMMAND_ROLE_MAP[commandName] || [];
|
|
599
|
-
}
|
|
600
|
-
const required = ["coder"];
|
|
601
|
-
for (const [pipelineKey, roleName] of RUN_PIPELINE_ROLES) {
|
|
602
|
-
const pipelineEntry = config?.pipeline?.[pipelineKey];
|
|
603
|
-
// reviewer defaults to enabled (only excluded if explicitly false)
|
|
604
|
-
const isEnabled = pipelineKey === "reviewer"
|
|
605
|
-
? pipelineEntry?.enabled !== false
|
|
606
|
-
: Boolean(pipelineEntry?.enabled);
|
|
607
|
-
if (isEnabled) required.push(roleName);
|
|
608
|
-
}
|
|
609
|
-
return required;
|
|
610
|
-
}
|
|
611
|
-
|
|
612
|
-
export function validateConfig(config, commandName = "run") {
|
|
613
|
-
const errors = [];
|
|
614
|
-
if (!new Set(["paranoid", "strict", "standard", "relaxed", "custom"]).has(config.review_mode)) {
|
|
615
|
-
errors.push(`Invalid review_mode: ${config.review_mode}`);
|
|
616
|
-
}
|
|
617
|
-
if (!new Set(["tdd", "standard"]).has(config.development?.methodology)) {
|
|
618
|
-
errors.push(`Invalid development.methodology: ${config.development?.methodology}`);
|
|
619
|
-
}
|
|
620
|
-
|
|
621
|
-
const requiredRoles = requiredRolesFor(commandName, config);
|
|
622
|
-
for (const role of requiredRoles) {
|
|
623
|
-
const { provider } = resolveRole(config, role);
|
|
624
|
-
if (!provider) {
|
|
625
|
-
errors.push(
|
|
626
|
-
`Missing provider for required role '${role}'. Set 'roles.${role}.provider' or pass '--${role} <name>'`
|
|
627
|
-
);
|
|
628
|
-
}
|
|
629
|
-
}
|
|
630
|
-
|
|
631
|
-
if (errors.length > 0) {
|
|
632
|
-
throw new Error(errors.join("\n"));
|
|
633
|
-
}
|
|
634
|
-
|
|
635
|
-
return config;
|
|
636
|
-
}
|
|
24
|
+
export { DEFAULTS } from "./config/defaults.js";
|
|
25
|
+
export {
|
|
26
|
+
getConfigPath,
|
|
27
|
+
getProjectConfigPath,
|
|
28
|
+
loadProjectConfig,
|
|
29
|
+
loadConfig,
|
|
30
|
+
writeConfig,
|
|
31
|
+
} from "./config/loader.js";
|
|
32
|
+
export { applyRunOverrides } from "./config/overrides.js";
|
|
33
|
+
export { resolveRole, validateConfig } from "./config/role-resolver.js";
|
|
34
|
+
export { safeParseConfig, formatConfigIssues } from "./config/schema.js";
|
package/src/git/automation.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Extracted from orchestrator.js for testability and reuse.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import { addCheckpoint } from "../session
|
|
6
|
+
import { addCheckpoint } from "../session/store.js";
|
|
7
7
|
import {
|
|
8
8
|
ensureGitRepo,
|
|
9
9
|
currentBranch,
|
|
@@ -80,7 +80,7 @@ export async function prepareGitAutomation({ config, task, logger, session }) {
|
|
|
80
80
|
return { enabled: true, branch, baseBranch, autoRebase };
|
|
81
81
|
}
|
|
82
82
|
|
|
83
|
-
export function buildPrBody({ task, stageResults }) {
|
|
83
|
+
export function buildPrBody({ task: _task, stageResults }) {
|
|
84
84
|
const sections = ["Created by Karajan Code."];
|
|
85
85
|
|
|
86
86
|
const approach = stageResults?.planner?.approach;
|