karajan-code 1.53.0 → 1.54.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 CHANGED
@@ -258,7 +258,7 @@ Not nostalgia, not stubbornness. I've been using JavaScript since 1997, when Bre
258
258
  git clone https://github.com/manufosela/karajan-code.git
259
259
  cd karajan-code
260
260
  npm install
261
- npm test # Run ~2473 tests with Vitest
261
+ npm test # Run ~2599 tests with Vitest
262
262
  npm run validate # Lint + test
263
263
  ```
264
264
 
package/docs/README.es.md CHANGED
@@ -231,7 +231,7 @@ No es nostalgia ni cabezoneria. Es que llevo usando JavaScript desde 1997, cuand
231
231
  git clone https://github.com/manufosela/karajan-code.git
232
232
  cd karajan-code
233
233
  npm install
234
- npm test # Ejecutar ~2473 tests con Vitest
234
+ npm test # Ejecutar ~2599 tests con Vitest
235
235
  npm run validate # Lint + test
236
236
  ```
237
237
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "karajan-code",
3
- "version": "1.53.0",
3
+ "version": "1.54.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
@@ -117,6 +117,7 @@ program
117
117
  .option("--no-auto-simplify", "Force full pipeline regardless of triage level")
118
118
  .option("--smart-models", "Enable smart model selection based on triage complexity")
119
119
  .option("--no-smart-models", "Disable smart model selection")
120
+ .option("--design", "Activate design refactoring mode (impeccable role applies design changes)")
120
121
  .option("--dry-run", "Show what would be executed without running anything")
121
122
  .option("--json", "Output JSON only (no styled display)")
122
123
  .option("-q, --quiet", "Show only stage status lines, suppress raw agent output (default)")
@@ -0,0 +1,81 @@
1
+ /**
2
+ * MCP response compressor — reduces token consumption by stripping
3
+ * verbose fields from list payloads and truncating oversized arrays.
4
+ */
5
+
6
+ // Fields to strip from list items (keep in single-item detail views)
7
+ const STRIP_FROM_LIST_ITEMS = [
8
+ "descriptionStructured", "acceptanceCriteriaStructured",
9
+ "implementationPlan", "implementationNotes",
10
+ "developmentInstructions", "raw", "textSummary",
11
+ "workCycles", "config_snapshot", "paused_state",
12
+ "checkpoints"
13
+ ];
14
+
15
+ // Fields to strip from all responses (never useful to the LLM)
16
+ const ALWAYS_STRIP = [
17
+ "firebaseId", "cardType", "group", "createdBy", "updatedBy",
18
+ "_instance"
19
+ ];
20
+
21
+ const MAX_ARRAY_ITEMS = 20;
22
+ const MAX_COMMITS = 5;
23
+ const MAX_FINDINGS = 10;
24
+
25
+ export function compressResponse(data, toolName = "") {
26
+ if (!data || typeof data !== "object") return data;
27
+ if (Array.isArray(data)) return compressArray(data);
28
+
29
+ const compressed = { ...data };
30
+
31
+ // Strip always-remove fields
32
+ for (const key of ALWAYS_STRIP) delete compressed[key];
33
+
34
+ // Compress nested arrays and objects
35
+ for (const [key, value] of Object.entries(compressed)) {
36
+ if (Array.isArray(value)) {
37
+ if (key === "commits") compressed[key] = truncateFromEnd(value, MAX_COMMITS);
38
+ else if (key === "findings" || key === "blocking_issues") compressed[key] = truncateFromStart(value, MAX_FINDINGS);
39
+ else compressed[key] = compressArray(value);
40
+ } else if (value && typeof value === "object") {
41
+ compressed[key] = compressResponse(value, toolName);
42
+ }
43
+ }
44
+
45
+ return compressed;
46
+ }
47
+
48
+ function compressArray(arr) {
49
+ if (!Array.isArray(arr) || arr.length === 0) return arr;
50
+
51
+ // Strip verbose fields from list items
52
+ const stripped = arr.map(item => {
53
+ if (!item || typeof item !== "object") return item;
54
+ const clean = { ...item };
55
+ for (const key of STRIP_FROM_LIST_ITEMS) delete clean[key];
56
+ for (const key of ALWAYS_STRIP) delete clean[key];
57
+ return clean;
58
+ });
59
+
60
+ if (stripped.length <= MAX_ARRAY_ITEMS) return stripped;
61
+
62
+ const truncated = stripped.slice(0, MAX_ARRAY_ITEMS);
63
+ truncated.push({ _truncated: true, _total: arr.length, _showing: MAX_ARRAY_ITEMS });
64
+ return truncated;
65
+ }
66
+
67
+ function truncateFromEnd(arr, max) {
68
+ if (arr.length <= max) return arr;
69
+ return [{ _note: `${arr.length - max} earlier items omitted` }, ...arr.slice(-max)];
70
+ }
71
+
72
+ function truncateFromStart(arr, max) {
73
+ if (arr.length <= max) return arr;
74
+ const truncated = arr.slice(0, max);
75
+ truncated.push({ _note: `... and ${arr.length - max} more` });
76
+ return truncated;
77
+ }
78
+
79
+ export function compactStringify(data) {
80
+ return JSON.stringify(data);
81
+ }
package/src/mcp/run-kj.js CHANGED
@@ -43,6 +43,7 @@ export async function runKjCommand({ command, commandArgs = [], options = {}, en
43
43
  normalizeBoolFlag(options.enableTester, "--enable-tester", args);
44
44
  normalizeBoolFlag(options.enableSecurity, "--enable-security", args);
45
45
  normalizeBoolFlag(options.enableImpeccable, "--enable-impeccable", args);
46
+ normalizeBoolFlag(options.design, "--design", args);
46
47
  normalizeBoolFlag(options.enableTriage, "--enable-triage", args);
47
48
  normalizeBoolFlag(options.enableDiscover, "--enable-discover", args);
48
49
  normalizeBoolFlag(options.enableArchitect, "--enable-architect", args);
@@ -9,6 +9,7 @@ import fs from "node:fs/promises";
9
9
  import { loadConfig, applyRunOverrides, validateConfig } from "../config.js";
10
10
  import { currentBranch } from "../utils/git.js";
11
11
  import { buildProgressNotifier } from "./progress.js";
12
+ import { compressResponse, compactStringify } from "./response-compressor.js";
12
13
 
13
14
  // ── Sub-module re-exports ────────────────────────────────────────────
14
15
  export { handleRunDirect, handleResumeDirect, validateResumeAnswer, handleRun, handleResume } from "./handlers/run-handler.js";
@@ -58,8 +59,9 @@ export function asObject(value) {
58
59
  }
59
60
 
60
61
  export function responseText(payload) {
62
+ const compressed = compressResponse(payload);
61
63
  return {
62
- content: [{ type: "text", text: JSON.stringify(payload, null, 2) }]
64
+ content: [{ type: "text", text: compactStringify(compressed) }]
63
65
  };
64
66
  }
65
67
 
package/src/mcp/tools.js CHANGED
@@ -100,6 +100,7 @@ export const tools = [
100
100
  taskType: { type: "string", enum: ["sw", "infra", "doc", "add-tests", "refactor"], description: "Explicit task type for policy resolution. Overrides triage classification." },
101
101
  quiet: { type: "boolean", description: "Suppress raw agent output lines, show only stage status (default: true). Set false for verbose output." },
102
102
  noSonar: { type: "boolean" },
103
+ design: { type: "boolean", description: "Activate design refactoring mode. Impeccable role applies design changes instead of just auditing." },
103
104
  enableSonarcloud: { type: "boolean", description: "Enable SonarCloud scan (complementary to SonarQube)" },
104
105
  kjHome: { type: "string" },
105
106
  sonarToken: { type: "string" },
@@ -247,17 +247,21 @@ export async function runSecurityStage({ config, logger, emitter, eventBase, ses
247
247
  return { action: "ok", stageResult: { ok: true, summary: securityOutput.summary || "No vulnerabilities found" } };
248
248
  }
249
249
 
250
- export async function runImpeccableStage({ config, logger, emitter, eventBase, session, coderRole, trackBudget, iteration, task, diff }) {
250
+ export async function runImpeccableStage({ config, logger, emitter, eventBase, session, coderRole, trackBudget, iteration, task, diff, mode = "audit" }) {
251
251
  logger.setContext({ iteration, stage: "impeccable" });
252
+ const isRefactoring = mode === "refactoring";
253
+ const startMessage = isRefactoring
254
+ ? "Impeccable applying design refactoring"
255
+ : "Impeccable auditing frontend design quality";
252
256
  emitProgress(
253
257
  emitter,
254
258
  makeEvent("impeccable:start", { ...eventBase, stage: "impeccable" }, {
255
- message: "Impeccable auditing frontend design quality",
256
- detail: { provider: config?.roles?.impeccable?.provider || coderRole.provider, executorType: "agent" }
259
+ message: startMessage,
260
+ detail: { provider: config?.roles?.impeccable?.provider || coderRole.provider, executorType: "agent", mode }
257
261
  })
258
262
  );
259
263
 
260
- const impeccable = new ImpeccableRole({ config, logger, emitter });
264
+ const impeccable = new ImpeccableRole({ config, logger, emitter, mode });
261
265
  await impeccable.init({ task, iteration });
262
266
  const impeccableStart = Date.now();
263
267
  let impeccableOutput;
@@ -262,6 +262,12 @@ function applyFlagOverrides(pipelineFlags, flags) {
262
262
  if (flags.enableTester !== undefined) pipelineFlags.testerEnabled = Boolean(flags.enableTester);
263
263
  if (flags.enableSecurity !== undefined) pipelineFlags.securityEnabled = Boolean(flags.enableSecurity);
264
264
  if (flags.enableImpeccable !== undefined) pipelineFlags.impeccableEnabled = Boolean(flags.enableImpeccable);
265
+
266
+ // --design flag: force-enable impeccable in refactoring mode
267
+ if (flags.design) {
268
+ pipelineFlags.impeccableEnabled = true;
269
+ pipelineFlags.impeccableMode = "refactoring";
270
+ }
265
271
  }
266
272
 
267
273
  function resolvePipelinePolicies({ flags, config, stageResults, emitter, eventBase, session, pipelineFlags }) {
@@ -1149,9 +1155,10 @@ async function runQualityGateStages({ config, logger, emitter, eventBase, sessio
1149
1155
 
1150
1156
  if (pipelineFlags?.impeccableEnabled) {
1151
1157
  const diff = await generateDiff({ baseRef: session.session_start_sha });
1158
+ const impeccableMode = pipelineFlags?.impeccableMode || "audit";
1152
1159
  const impeccableResult = await runImpeccableStage({
1153
1160
  config, logger, emitter, eventBase, session, coderRole, trackBudget,
1154
- iteration: i, task, diff
1161
+ iteration: i, task, diff, mode: impeccableMode
1155
1162
  });
1156
1163
  if (impeccableResult.stageResult) {
1157
1164
  stageResults.impeccable = impeccableResult.stageResult;
@@ -59,9 +59,11 @@ function buildSummary(parsed) {
59
59
  }
60
60
 
61
61
  export class ImpeccableRole extends BaseRole {
62
- constructor({ config, logger, emitter = null, createAgentFn = null }) {
63
- super({ name: "impeccable", config, logger, emitter });
62
+ constructor({ config, logger, emitter = null, createAgentFn = null, mode = "audit" }) {
63
+ const roleName = mode === "refactoring" ? "impeccable-design" : "impeccable";
64
+ super({ name: roleName, config, logger, emitter });
64
65
  this._createAgent = createAgentFn || defaultCreateAgent;
66
+ this.mode = mode;
65
67
  }
66
68
 
67
69
  async execute(input) {
@@ -0,0 +1,87 @@
1
+ # Impeccable Design (Refactoring Mode)
2
+
3
+ You are refactoring the frontend design. Apply ALL of these improvements:
4
+
5
+ ## Scope constraint
6
+
7
+ - **ONLY modify files present in the diff.** Do not touch files that were not changed.
8
+ - If no frontend files (.html, .css, .astro, .jsx, .tsx, .vue, .svelte, .lit, .js with DOM manipulation) are in the diff, report APPROVED immediately with 0 issues.
9
+
10
+ ## Input
11
+
12
+ - **Task**: {{task}}
13
+ - **Diff**: {{diff}}
14
+ - **Context**: {{context}}
15
+
16
+ ## Visual Hierarchy
17
+ - Establish clear heading levels (size, weight, color)
18
+ - Group related elements with consistent spacing
19
+ - Use whitespace to separate sections
20
+
21
+ ## Spacing & Alignment
22
+ - Consistent padding/margin using a spacing scale (4px, 8px, 16px, 24px, 32px)
23
+ - Align elements to a grid
24
+ - Remove arbitrary pixel values
25
+
26
+ ## Responsive
27
+ - Mobile-first approach
28
+ - Fluid layouts with relative units (rem, %, vw)
29
+ - Breakpoints at 640px, 768px, 1024px, 1280px
30
+
31
+ ## Accessibility
32
+ - Color contrast ratio >= 4.5:1 for text
33
+ - All interactive elements keyboard-focusable
34
+ - Labels on all form inputs
35
+ - Alt text on all images
36
+
37
+ ## Micro-interactions
38
+ - Hover/focus states on all interactive elements
39
+ - Smooth transitions (150-300ms)
40
+ - Loading states for async operations
41
+
42
+ ## Theming
43
+ - CSS custom properties for colors, fonts, spacing
44
+ - Dark mode support if applicable
45
+ - Consistent color palette (max 5 primary colors)
46
+
47
+ ## Rules
48
+ - Each fix must be minimal and targeted (Edit, not Write)
49
+ - Only use Read, Edit, Grep, Glob, and Bash tools
50
+ - Verify each fix with `git diff` to confirm only intended lines changed
51
+ - Apply changes directly. Do not just list issues.
52
+
53
+ ## Report
54
+
55
+ Output a strict JSON object:
56
+
57
+ ```json
58
+ {
59
+ "ok": true,
60
+ "result": {
61
+ "verdict": "IMPROVED",
62
+ "issuesFound": 3,
63
+ "issuesFixed": 3,
64
+ "categories": {
65
+ "visualHierarchy": 0,
66
+ "spacing": 1,
67
+ "responsive": 1,
68
+ "a11y": 1,
69
+ "microInteractions": 0,
70
+ "theming": 0
71
+ },
72
+ "changes": [
73
+ {
74
+ "file": "src/components/Button.astro",
75
+ "issue": "Arbitrary padding values",
76
+ "fix": "Replaced with spacing scale (16px)",
77
+ "category": "spacing"
78
+ }
79
+ ]
80
+ },
81
+ "summary": "3 design issues found and fixed (1 spacing, 1 responsive, 1 a11y)"
82
+ }
83
+ ```
84
+
85
+ ### Verdict rules
86
+ - **APPROVED** — No frontend design issues found (issuesFound === 0)
87
+ - **IMPROVED** — Issues were found and fixes were applied (issuesFixed > 0)