karajan-code 1.16.0 → 1.18.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 +1 -1
- package/src/activity-log.js +13 -13
- package/src/agents/availability.js +2 -3
- package/src/agents/claude-agent.js +42 -21
- package/src/agents/model-registry.js +1 -1
- package/src/becaria/dispatch.js +1 -1
- package/src/becaria/repo.js +3 -3
- package/src/cli.js +5 -2
- package/src/commands/doctor.js +154 -108
- package/src/commands/init.js +101 -90
- package/src/commands/plan.js +1 -1
- package/src/commands/report.js +77 -71
- package/src/commands/roles.js +0 -1
- package/src/commands/run.js +2 -3
- package/src/config.js +174 -93
- package/src/git/automation.js +3 -4
- package/src/guards/intent-guard.js +123 -0
- package/src/guards/output-guard.js +158 -0
- package/src/guards/perf-guard.js +126 -0
- package/src/guards/policy-resolver.js +3 -3
- package/src/mcp/orphan-guard.js +1 -2
- package/src/mcp/progress.js +4 -3
- package/src/mcp/run-kj.js +1 -0
- package/src/mcp/server-handlers.js +242 -253
- package/src/mcp/server.js +4 -3
- package/src/mcp/tools.js +2 -0
- package/src/orchestrator/agent-fallback.js +1 -3
- package/src/orchestrator/iteration-stages.js +206 -170
- package/src/orchestrator/pre-loop-stages.js +200 -34
- package/src/orchestrator/solomon-rules.js +2 -2
- package/src/orchestrator.js +902 -746
- package/src/planning-game/adapter.js +23 -20
- package/src/planning-game/architect-adrs.js +45 -0
- package/src/planning-game/client.js +15 -1
- package/src/planning-game/decomposition.js +7 -5
- package/src/prompts/architect.js +88 -0
- package/src/prompts/discover.js +54 -53
- package/src/prompts/planner.js +53 -33
- package/src/prompts/triage.js +8 -16
- package/src/review/parser.js +18 -19
- package/src/review/profiles.js +2 -2
- package/src/review/schema.js +3 -3
- package/src/review/scope-filter.js +3 -4
- package/src/roles/architect-role.js +122 -0
- package/src/roles/commiter-role.js +2 -2
- package/src/roles/discover-role.js +59 -67
- package/src/roles/index.js +1 -0
- package/src/roles/planner-role.js +54 -38
- package/src/roles/refactorer-role.js +8 -7
- package/src/roles/researcher-role.js +6 -7
- package/src/roles/reviewer-role.js +4 -5
- package/src/roles/security-role.js +3 -4
- package/src/roles/solomon-role.js +6 -18
- package/src/roles/sonar-role.js +5 -1
- package/src/roles/tester-role.js +8 -5
- package/src/roles/triage-role.js +2 -2
- package/src/session-cleanup.js +29 -24
- package/src/session-store.js +1 -1
- package/src/sonar/api.js +1 -1
- package/src/sonar/manager.js +1 -1
- package/src/sonar/project-key.js +5 -5
- package/src/sonar/scanner.js +34 -65
- package/src/utils/display.js +312 -272
- package/src/utils/git.js +3 -3
- package/src/utils/logger.js +6 -1
- package/src/utils/model-selector.js +5 -5
- package/src/utils/process.js +80 -102
- package/src/utils/rate-limit-detector.js +13 -13
- package/src/utils/run-log.js +55 -52
- package/templates/kj.config.yml +33 -0
- package/templates/roles/architect.md +62 -0
- package/templates/roles/planner.md +1 -0
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
const FRONTEND_EXTENSIONS = new Set([".html", ".htm", ".css", ".jsx", ".tsx", ".astro", ".vue", ".svelte"]);
|
|
2
|
+
|
|
3
|
+
// Built-in perf anti-patterns (applied to added lines in frontend files)
|
|
4
|
+
const PERF_PATTERNS = [
|
|
5
|
+
{ id: "img-no-dimensions", pattern: /<img\b(?![^>]*\bwidth\b)(?![^>]*\bheight\b)[^>]*>/i, severity: "warning", message: "Image without width/height attributes (causes CLS)" },
|
|
6
|
+
{ id: "img-no-lazy", pattern: /<img\b(?![^>]*\bloading\s*=)(?![^>]*\bfetchpriority\s*=)[^>]*>/i, severity: "info", message: "Image without loading=\"lazy\" or fetchpriority (consider lazy loading)" },
|
|
7
|
+
{ id: "script-no-defer", pattern: /<script\b(?![^>]*\b(?:defer|async)\b)(?![^>]*type\s*=\s*["']module["'])[^>]*src\s*=/i, severity: "warning", message: "External script without defer/async (render-blocking)" },
|
|
8
|
+
{ id: "font-no-display", pattern: /@font-face\s*\{(?![^}]*font-display)/i, severity: "warning", message: "@font-face without font-display (causes FOIT)" },
|
|
9
|
+
{ id: "css-import", pattern: /@import\s+(?:url\()?["'](?!.*\.module\.)/i, severity: "info", message: "CSS @import (causes sequential loading, prefer <link>)" },
|
|
10
|
+
{ id: "inline-style-large", pattern: /style\s*=\s*["'][^"']{200,}["']/i, severity: "warning", message: "Large inline style (>200 chars, consider external CSS)" },
|
|
11
|
+
{ id: "document-write", pattern: /document\.write\s*\(/, severity: "warning", message: "document.write() blocks parsing and degrades performance" },
|
|
12
|
+
];
|
|
13
|
+
|
|
14
|
+
// Patterns for package.json changes (heavy dependencies added)
|
|
15
|
+
const HEAVY_DEPS = [
|
|
16
|
+
{ id: "heavy-moment", pattern: /"moment"/, severity: "info", message: "moment.js added (consider dayjs or date-fns for smaller bundle)" },
|
|
17
|
+
{ id: "heavy-lodash", pattern: /"lodash"(?!\/)/, severity: "info", message: "Full lodash added (consider lodash-es or individual imports)" },
|
|
18
|
+
{ id: "heavy-jquery", pattern: /"jquery"/, severity: "info", message: "jQuery added (consider native DOM APIs)" },
|
|
19
|
+
];
|
|
20
|
+
|
|
21
|
+
function getExtension(filePath) {
|
|
22
|
+
if (!filePath) return "";
|
|
23
|
+
const dot = filePath.lastIndexOf(".");
|
|
24
|
+
return dot >= 0 ? filePath.slice(dot).toLowerCase() : "";
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Check if any modified file in the diff is a frontend file
|
|
29
|
+
*/
|
|
30
|
+
export function hasFrontendFiles(diff) {
|
|
31
|
+
if (!diff) return false;
|
|
32
|
+
for (const line of diff.split("\n")) {
|
|
33
|
+
if (line.startsWith("+++ b/")) {
|
|
34
|
+
const ext = getExtension(line.slice(6));
|
|
35
|
+
if (FRONTEND_EXTENSIONS.has(ext)) return true;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Extract added lines grouped by file from a unified diff
|
|
43
|
+
*/
|
|
44
|
+
function extractAddedLinesByFile(diff) {
|
|
45
|
+
const results = [];
|
|
46
|
+
let currentFile = null;
|
|
47
|
+
let lineNum = 0;
|
|
48
|
+
|
|
49
|
+
for (const line of diff.split("\n")) {
|
|
50
|
+
if (line.startsWith("+++ b/")) {
|
|
51
|
+
currentFile = line.slice(6);
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
if (line.startsWith("@@ ")) {
|
|
55
|
+
const match = /@@ -\d+(?:,\d+)? \+(\d+)/.exec(line);
|
|
56
|
+
lineNum = match ? Number.parseInt(match[1], 10) - 1 : 0;
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
if (line.startsWith("+") && !line.startsWith("+++")) {
|
|
60
|
+
lineNum += 1;
|
|
61
|
+
results.push({ file: currentFile, line: lineNum, content: line.slice(1) });
|
|
62
|
+
} else if (!line.startsWith("-")) {
|
|
63
|
+
lineNum += 1;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return results;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Scan diff for frontend performance anti-patterns.
|
|
71
|
+
* Returns { pass: boolean, violations: [...], skipped: boolean }
|
|
72
|
+
*/
|
|
73
|
+
export function scanPerfDiff(diff, config = {}) {
|
|
74
|
+
if (!diff || typeof diff !== "string") {
|
|
75
|
+
return { pass: true, violations: [], skipped: true };
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (!hasFrontendFiles(diff)) {
|
|
79
|
+
return { pass: true, violations: [], skipped: true };
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const customPatterns = Array.isArray(config?.guards?.perf?.patterns)
|
|
83
|
+
? config.guards.perf.patterns.map(p => ({
|
|
84
|
+
id: p.id || "custom-perf",
|
|
85
|
+
pattern: typeof p.pattern === "string" ? new RegExp(p.pattern, p.flags || "i") : p.pattern,
|
|
86
|
+
severity: p.severity || "warning",
|
|
87
|
+
message: p.message || "Custom perf pattern matched",
|
|
88
|
+
}))
|
|
89
|
+
: [];
|
|
90
|
+
|
|
91
|
+
const allPatterns = [...PERF_PATTERNS, ...customPatterns];
|
|
92
|
+
const addedLines = extractAddedLinesByFile(diff);
|
|
93
|
+
const violations = [];
|
|
94
|
+
|
|
95
|
+
for (const { file, line, content } of addedLines) {
|
|
96
|
+
const ext = getExtension(file);
|
|
97
|
+
const isFrontend = FRONTEND_EXTENSIONS.has(ext);
|
|
98
|
+
const isPackageJson = file?.endsWith("package.json");
|
|
99
|
+
|
|
100
|
+
if (isFrontend) {
|
|
101
|
+
for (const { id, pattern, severity, message } of allPatterns) {
|
|
102
|
+
if (pattern.test(content)) {
|
|
103
|
+
violations.push({ id, severity, file, line, message, matchedContent: content.trim().slice(0, 200) });
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (isPackageJson) {
|
|
109
|
+
for (const { id, pattern, severity, message } of HEAVY_DEPS) {
|
|
110
|
+
if (pattern.test(content)) {
|
|
111
|
+
violations.push({ id, severity, file, line, message, matchedContent: content.trim().slice(0, 200) });
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// perf-guard is advisory by default — only blocks on critical (none built-in are critical)
|
|
118
|
+
const blockOnWarning = Boolean(config?.guards?.perf?.block_on_warning);
|
|
119
|
+
const hasCritical = violations.some(v => v.severity === "critical");
|
|
120
|
+
const hasWarning = violations.some(v => v.severity === "warning");
|
|
121
|
+
const pass = !hasCritical && !(blockOnWarning && hasWarning);
|
|
122
|
+
|
|
123
|
+
return { pass, violations, skipped: false };
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export { PERF_PATTERNS, HEAVY_DEPS, FRONTEND_EXTENSIONS };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export const VALID_TASK_TYPES = ["sw", "infra", "doc", "add-tests", "refactor"];
|
|
1
|
+
export const VALID_TASK_TYPES = new Set(["sw", "infra", "doc", "add-tests", "refactor"]);
|
|
2
2
|
|
|
3
3
|
export const DEFAULT_POLICIES = {
|
|
4
4
|
sw: { tdd: true, sonar: true, reviewer: true, testsRequired: true },
|
|
@@ -16,7 +16,7 @@ const FALLBACK_TYPE = "sw";
|
|
|
16
16
|
* configOverrides optionally merges over defaults per taskType.
|
|
17
17
|
*/
|
|
18
18
|
export function resolvePolicies(taskType, configOverrides) {
|
|
19
|
-
const resolvedType = VALID_TASK_TYPES.
|
|
19
|
+
const resolvedType = VALID_TASK_TYPES.has(taskType) ? taskType : FALLBACK_TYPE;
|
|
20
20
|
const base = { ...DEFAULT_POLICIES[resolvedType] };
|
|
21
21
|
const overrides = configOverrides?.[resolvedType];
|
|
22
22
|
if (overrides && typeof overrides === "object") {
|
|
@@ -31,7 +31,7 @@ export function resolvePolicies(taskType, configOverrides) {
|
|
|
31
31
|
* orchestrator to determine which pipeline stages to enable/disable.
|
|
32
32
|
*/
|
|
33
33
|
export function applyPolicies({ taskType, policies } = {}) {
|
|
34
|
-
const resolvedType = VALID_TASK_TYPES.
|
|
34
|
+
const resolvedType = VALID_TASK_TYPES.has(taskType) ? taskType : FALLBACK_TYPE;
|
|
35
35
|
const resolved = resolvePolicies(taskType, policies);
|
|
36
36
|
return { taskType: resolvedType, ...resolved };
|
|
37
37
|
}
|
package/src/mcp/orphan-guard.js
CHANGED
package/src/mcp/progress.js
CHANGED
|
@@ -57,7 +57,7 @@ export function buildPipelineTracker(config, emitter) {
|
|
|
57
57
|
};
|
|
58
58
|
|
|
59
59
|
emitter.on("progress", (event) => {
|
|
60
|
-
const match =
|
|
60
|
+
const match = /^(\w+):(start|end)$/.exec(event.type ?? "");
|
|
61
61
|
if (!match) return;
|
|
62
62
|
|
|
63
63
|
const [, name, phase] = match;
|
|
@@ -127,9 +127,10 @@ export function buildProgressNotifier(extra) {
|
|
|
127
127
|
if (idx < 0) return;
|
|
128
128
|
|
|
129
129
|
const iteration = event.iteration || event.detail?.iteration;
|
|
130
|
+
const baseMessage = event.message || event.type;
|
|
130
131
|
const message = iteration
|
|
131
|
-
? `[${event.iteration}] ${
|
|
132
|
-
:
|
|
132
|
+
? `[${event.iteration}] ${baseMessage}`
|
|
133
|
+
: baseMessage;
|
|
133
134
|
|
|
134
135
|
try {
|
|
135
136
|
extra.sendNotification({
|
package/src/mcp/run-kj.js
CHANGED
|
@@ -44,6 +44,7 @@ export async function runKjCommand({ command, commandArgs = [], options = {}, en
|
|
|
44
44
|
normalizeBoolFlag(options.enableSecurity, "--enable-security", args);
|
|
45
45
|
normalizeBoolFlag(options.enableTriage, "--enable-triage", args);
|
|
46
46
|
normalizeBoolFlag(options.enableDiscover, "--enable-discover", args);
|
|
47
|
+
normalizeBoolFlag(options.enableArchitect, "--enable-architect", args);
|
|
47
48
|
normalizeBoolFlag(options.enableSerena, "--enable-serena", args);
|
|
48
49
|
normalizeBoolFlag(options.autoCommit, "--auto-commit", args);
|
|
49
50
|
normalizeBoolFlag(options.autoPush, "--auto-push", args);
|