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 +1 -1
- package/docs/README.es.md +1 -1
- package/package.json +1 -1
- package/src/cli.js +1 -0
- package/src/mcp/response-compressor.js +81 -0
- package/src/mcp/run-kj.js +1 -0
- package/src/mcp/server-handlers.js +3 -1
- package/src/mcp/tools.js +1 -0
- package/src/orchestrator/post-loop-stages.js +8 -4
- package/src/orchestrator.js +8 -1
- package/src/roles/impeccable-role.js +4 -2
- package/templates/roles/impeccable-design.md +87 -0
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 ~
|
|
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 ~
|
|
234
|
+
npm test # Ejecutar ~2599 tests con Vitest
|
|
235
235
|
npm run validate # Lint + test
|
|
236
236
|
```
|
|
237
237
|
|
package/package.json
CHANGED
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:
|
|
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:
|
|
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;
|
package/src/orchestrator.js
CHANGED
|
@@ -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
|
-
|
|
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)
|