karajan-code 1.14.0 → 1.15.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "karajan-code",
3
- "version": "1.14.0",
3
+ "version": "1.15.0",
4
4
  "description": "Local multi-agent coding orchestrator with TDD, SonarQube, and code review pipeline",
5
5
  "type": "module",
6
6
  "license": "AGPL-3.0",
package/src/cli.js CHANGED
@@ -87,6 +87,7 @@ program
87
87
  .option("--auto-pr")
88
88
  .option("--enable-becaria", "Enable BecarIA Gateway (early PR + dispatch comments/reviews)")
89
89
  .option("--branch-prefix <prefix>")
90
+ .option("--task-type <type>", "Explicit task type: sw, infra, doc, add-tests, refactor")
90
91
  .option("--methodology <name>")
91
92
  .option("--no-auto-rebase")
92
93
  .option("--no-sonar")
package/src/mcp/run-kj.js CHANGED
@@ -48,6 +48,7 @@ export async function runKjCommand({ command, commandArgs = [], options = {}, en
48
48
  normalizeBoolFlag(options.autoPush, "--auto-push", args);
49
49
  normalizeBoolFlag(options.autoPr, "--auto-pr", args);
50
50
  if (options.autoRebase === false) args.push("--no-auto-rebase");
51
+ addOptionalValue(args, "--task-type", options.taskType);
51
52
  normalizeBoolFlag(options.noSonar, "--no-sonar", args);
52
53
  if (options.smartModels === true) args.push("--smart-models");
53
54
  if (options.smartModels === false) args.push("--no-smart-models");
@@ -565,6 +565,12 @@ export async function handleToolCall(name, args, server, extra) {
565
565
  if (!a.task) {
566
566
  return failPayload("Missing required field: task");
567
567
  }
568
+ if (a.taskType) {
569
+ const validTypes = ["sw", "infra", "doc", "add-tests", "refactor"];
570
+ if (!validTypes.includes(a.taskType)) {
571
+ return failPayload(`Invalid taskType "${a.taskType}". Valid values: ${validTypes.join(", ")}`);
572
+ }
573
+ }
568
574
  if (!isPreflightAcked()) {
569
575
  const { config } = await loadConfig();
570
576
  const { listAgents } = await import("../commands/agents.js");
package/src/mcp/tools.js CHANGED
@@ -88,6 +88,7 @@ export const tools = [
88
88
  branchPrefix: { type: "string" },
89
89
  smartModels: { type: "boolean", description: "Enable/disable smart model selection based on triage complexity" },
90
90
  checkpointInterval: { type: "number", description: "Minutes between interactive checkpoints (default: 5). Set 0 to disable." },
91
+ taskType: { type: "string", enum: ["sw", "infra", "doc", "add-tests", "refactor"], description: "Explicit task type for policy resolution. Overrides triage classification." },
91
92
  noSonar: { type: "boolean" },
92
93
  kjHome: { type: "string" },
93
94
  sonarToken: { type: "string" },
@@ -56,12 +56,14 @@ export async function runTriageStage({ config, logger, emitter, eventBase, sessi
56
56
  const recommendedRoles = new Set(triageOutput.result?.roles || []);
57
57
  const roleOverrides = {};
58
58
  if (triageOutput.ok) {
59
- roleOverrides.plannerEnabled = recommendedRoles.has("planner");
60
- roleOverrides.researcherEnabled = recommendedRoles.has("researcher");
61
- roleOverrides.refactorerEnabled = recommendedRoles.has("refactorer");
62
- roleOverrides.reviewerEnabled = recommendedRoles.has("reviewer");
63
- roleOverrides.testerEnabled = recommendedRoles.has("tester");
64
- roleOverrides.securityEnabled = recommendedRoles.has("security");
59
+ // Triage can activate roles, but cannot deactivate roles explicitly enabled in pipeline config
60
+ const p = config.pipeline || {};
61
+ roleOverrides.plannerEnabled = recommendedRoles.has("planner") || Boolean(p.planner?.enabled);
62
+ roleOverrides.researcherEnabled = recommendedRoles.has("researcher") || Boolean(p.researcher?.enabled);
63
+ roleOverrides.refactorerEnabled = recommendedRoles.has("refactorer") || Boolean(p.refactorer?.enabled);
64
+ roleOverrides.reviewerEnabled = recommendedRoles.has("reviewer") || Boolean(p.reviewer?.enabled);
65
+ roleOverrides.testerEnabled = recommendedRoles.has("tester") || Boolean(p.tester?.enabled);
66
+ roleOverrides.securityEnabled = recommendedRoles.has("security") || Boolean(p.security?.enabled);
65
67
  }
66
68
 
67
69
  const shouldDecompose = triageOutput.result?.shouldDecompose || false;
@@ -72,6 +74,7 @@ export async function runTriageStage({ config, logger, emitter, eventBase, sessi
72
74
  level: triageOutput.result?.level || null,
73
75
  roles: Array.from(recommendedRoles),
74
76
  reasoning: triageOutput.result?.reasoning || null,
77
+ taskType: triageOutput.result?.taskType || "sw",
75
78
  shouldDecompose,
76
79
  subtasks
77
80
  };
@@ -44,7 +44,8 @@ export async function runFlow({ task, config, logger, flags = {}, emitter = null
44
44
  let testerEnabled = Boolean(config.pipeline?.tester?.enabled);
45
45
  let securityEnabled = Boolean(config.pipeline?.security?.enabled);
46
46
  let reviewerEnabled = config.pipeline?.reviewer?.enabled !== false;
47
- const triageEnabled = Boolean(config.pipeline?.triage?.enabled);
47
+ // Triage is always mandatory — it classifies taskType for policy resolution
48
+ const triageEnabled = true;
48
49
 
49
50
  // --- Dry-run: return summary without executing anything ---
50
51
  if (flags.dryRun) {
@@ -282,8 +283,9 @@ export async function runFlow({ task, config, logger, flags = {}, emitter = null
282
283
  if (flags.enableSecurity !== undefined) securityEnabled = Boolean(flags.enableSecurity);
283
284
 
284
285
  // --- Policy resolver: gate stages by taskType ---
286
+ // Priority: explicit flag > config > triage classification > default (sw)
285
287
  const resolvedPolicies = applyPolicies({
286
- taskType: flags.taskType || config.taskType || null,
288
+ taskType: flags.taskType || config.taskType || stageResults.triage?.taskType || null,
287
289
  policies: config.policies,
288
290
  });
289
291
  session.resolved_policies = resolvedPolicies;
@@ -47,10 +47,10 @@ export function buildTriagePrompt({ task, instructions, availableRoles }) {
47
47
  );
48
48
 
49
49
  sections.push(
50
- "Classify the task complexity, recommend only the necessary pipeline roles, and assess whether the task should be decomposed into smaller subtasks.",
50
+ "Classify the task complexity, determine its taskType, recommend only the necessary pipeline roles, and assess whether the task should be decomposed into smaller subtasks.",
51
51
  "Keep the reasoning short and practical.",
52
52
  "Return a single valid JSON object and nothing else.",
53
- 'JSON schema: {"level":"trivial|simple|medium|complex","roles":["planner|researcher|refactorer|reviewer|tester|security"],"reasoning":string,"shouldDecompose":boolean,"subtasks":string[]}'
53
+ 'JSON schema: {"level":"trivial|simple|medium|complex","roles":["planner|researcher|refactorer|reviewer|tester|security"],"taskType":"sw|infra|doc|add-tests|refactor","reasoning":string,"shouldDecompose":boolean,"subtasks":string[]}'
54
54
  );
55
55
 
56
56
  sections.push(`## Task\n${task}`);
@@ -1,9 +1,11 @@
1
1
  import { BaseRole } from "./base-role.js";
2
2
  import { createAgent as defaultCreateAgent } from "../agents/index.js";
3
3
  import { buildTriagePrompt } from "../prompts/triage.js";
4
+ import { VALID_TASK_TYPES } from "../guards/policy-resolver.js";
4
5
 
5
6
  const VALID_LEVELS = new Set(["trivial", "simple", "medium", "complex"]);
6
7
  const VALID_ROLES = new Set(["planner", "researcher", "refactorer", "reviewer", "tester", "security"]);
8
+ const FALLBACK_TASK_TYPE = "sw";
7
9
 
8
10
  function resolveProvider(config) {
9
11
  return (
@@ -74,6 +76,7 @@ export class TriageRole extends BaseRole {
74
76
  level: "medium",
75
77
  roles: ["reviewer"],
76
78
  reasoning: "Unstructured output, using safe defaults.",
79
+ taskType: FALLBACK_TASK_TYPE,
77
80
  provider,
78
81
  raw: result.output
79
82
  },
@@ -87,11 +90,13 @@ export class TriageRole extends BaseRole {
87
90
  const reasoning = String(parsed.reasoning || "").trim() || "No reasoning provided.";
88
91
  const shouldDecompose = Boolean(parsed.shouldDecompose);
89
92
  const subtasks = normalizeSubtasks(parsed.subtasks);
93
+ const taskType = VALID_TASK_TYPES.includes(parsed.taskType) ? parsed.taskType : FALLBACK_TASK_TYPE;
90
94
 
91
95
  const triageResult = {
92
96
  level,
93
97
  roles,
94
98
  reasoning,
99
+ taskType,
95
100
  provider
96
101
  };
97
102
 
@@ -116,6 +121,7 @@ export class TriageRole extends BaseRole {
116
121
  level: "medium",
117
122
  roles: ["reviewer"],
118
123
  reasoning: "Failed to parse triage output, using safe defaults.",
124
+ taskType: FALLBACK_TASK_TYPE,
119
125
  provider,
120
126
  raw: result.output
121
127
  },
@@ -8,6 +8,7 @@ Return a single valid JSON object and nothing else:
8
8
  ```json
9
9
  {
10
10
  "level": "trivial|simple|medium|complex",
11
+ "taskType": "sw|infra|doc|add-tests|refactor",
11
12
  "roles": ["planner", "researcher", "refactorer", "reviewer", "tester", "security"],
12
13
  "reasoning": "brief practical justification",
13
14
  "shouldDecompose": false,
@@ -15,7 +16,14 @@ Return a single valid JSON object and nothing else:
15
16
  }
16
17
  ```
17
18
 
18
- ## Classification guidance
19
+ ## Task type classification
20
+ - `sw`: writing or modifying business logic, features, APIs, components, services.
21
+ - `infra`: CI/CD, Docker, deploy scripts, build configuration, environment setup.
22
+ - `doc`: documentation, README, CHANGELOG, comments-only changes.
23
+ - `add-tests`: adding tests to existing code without changing functionality.
24
+ - `refactor`: restructuring code without changing external behavior.
25
+
26
+ ## Complexity classification
19
27
  - `trivial`: tiny, low-risk, straightforward. Usually no extra roles.
20
28
  - `simple`: limited scope with low risk. Usually reviewer only.
21
29
  - `medium`: moderate scope/risk. Reviewer required; optional planner/researcher.