karajan-code 2.7.4 → 2.8.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.
Files changed (165) hide show
  1. package/README.md +1 -1
  2. package/package.json +24 -4
  3. package/scripts/diet-baseline.mjs +170 -0
  4. package/scripts/regen-arch-stats.sh +71 -0
  5. package/src/agents/availability.js +15 -9
  6. package/src/agents/claude-agent.js +143 -45
  7. package/src/agents/model-registry.js +1 -1
  8. package/src/audit/agent-readiness.js +240 -0
  9. package/src/bootstrap.js +26 -1
  10. package/src/brain/decision-tracker.js +3 -1
  11. package/src/brain/decisor.js +1 -1
  12. package/src/canvas/parse-md.js +238 -0
  13. package/src/canvas/schema.js +101 -0
  14. package/src/canvas/to-flat.js +85 -0
  15. package/src/canvas/validate.js +163 -0
  16. package/src/checks/binaries.js +1 -1
  17. package/src/checks/ports.js +85 -31
  18. package/src/checks/sonar.js +1 -1
  19. package/src/checks/system.js +1 -1
  20. package/src/cli/_shared.js +30 -0
  21. package/src/cli/register-meta.js +204 -0
  22. package/src/cli/register-pipeline.js +212 -0
  23. package/src/cli/register-plan.js +152 -0
  24. package/src/cli/register-roles-skills.js +38 -0
  25. package/src/cli/register-sonar.js +28 -0
  26. package/src/cli.js +83 -468
  27. package/src/commands/agents.js +18 -6
  28. package/src/commands/architect.js +29 -2
  29. package/src/commands/audit.js +27 -3
  30. package/src/commands/board.js +160 -30
  31. package/src/commands/clean.js +83 -0
  32. package/src/commands/code.js +7 -2
  33. package/src/commands/discover.js +7 -2
  34. package/src/commands/init.js +2 -2
  35. package/src/commands/plan/_shared.js +40 -0
  36. package/src/commands/plan/add-hu.js +21 -0
  37. package/src/commands/plan/delete.js +9 -0
  38. package/src/commands/plan/generate.js +284 -0
  39. package/src/commands/plan/index.js +14 -0
  40. package/src/commands/plan/list.js +21 -0
  41. package/src/commands/plan/ready.js +46 -0
  42. package/src/commands/plan/remove-hu.js +17 -0
  43. package/src/commands/plan/show.js +81 -0
  44. package/src/commands/plan/validate.js +20 -0
  45. package/src/commands/plan.js +14 -291
  46. package/src/commands/researcher.js +7 -2
  47. package/src/commands/resume.js +24 -1
  48. package/src/commands/review.js +7 -2
  49. package/src/commands/run.js +52 -1
  50. package/src/commands/status.js +1 -1
  51. package/src/commands/sync.js +75 -0
  52. package/src/commands/triage.js +7 -2
  53. package/src/commands/undo.js +1 -1
  54. package/src/config/defaults.js +213 -0
  55. package/src/config/loader.js +137 -0
  56. package/src/config/overrides.js +199 -0
  57. package/src/config/role-resolver.js +159 -0
  58. package/src/config/test-harness.js +79 -0
  59. package/src/config.js +30 -632
  60. package/src/git/automation.js +2 -2
  61. package/src/guards/output-guard.js +0 -2
  62. package/src/hu/acceptance-runner.js +52 -10
  63. package/src/hu/store.js +11 -0
  64. package/src/mcp/handlers/direct-handlers.js +2 -5
  65. package/src/mcp/handlers/management-handlers.js +48 -4
  66. package/src/mcp/handlers/run-handler.js +9 -8
  67. package/src/mcp/preflight.js +32 -13
  68. package/src/mcp/server-handlers.js +4 -3
  69. package/src/mcp/suggest-handler.js +3 -2
  70. package/src/mcp/tools.js +15 -0
  71. package/src/orchestrator/agent-fallback.js +1 -1
  72. package/src/orchestrator/ci-integration.js +10 -10
  73. package/src/orchestrator/config-init.js +25 -7
  74. package/src/orchestrator/direct-actions.js +6 -2
  75. package/src/orchestrator/drivers/error-recovery.js +287 -0
  76. package/src/orchestrator/drivers/init-context.js +222 -0
  77. package/src/orchestrator/drivers/iteration-loop.js +311 -0
  78. package/src/orchestrator/drivers/iteration-phases/coder-and-refactorer.js +48 -0
  79. package/src/orchestrator/drivers/iteration-phases/guards.js +84 -0
  80. package/src/orchestrator/drivers/iteration-phases/handle-approved.js +29 -0
  81. package/src/orchestrator/drivers/iteration-phases/quality-gates.js +76 -0
  82. package/src/orchestrator/drivers/iteration-phases/reviewer-gate.js +60 -0
  83. package/src/orchestrator/drivers/post-loop.js +370 -0
  84. package/src/orchestrator/drivers/pre-loop-phases/auto-hu-batch.js +116 -0
  85. package/src/orchestrator/drivers/pre-loop-phases/config-deprecations.js +47 -0
  86. package/src/orchestrator/drivers/pre-loop-phases/ensure-addyosmani-skills.js +84 -0
  87. package/src/orchestrator/drivers/pre-loop-phases/inject-loaded-plan.js +268 -0
  88. package/src/orchestrator/drivers/pre-loop.js +434 -0
  89. package/src/orchestrator/drivers/run-hu-batch.js +367 -0
  90. package/src/orchestrator/flow-control.js +4 -3
  91. package/src/orchestrator/flow-runner.js +121 -2077
  92. package/src/orchestrator/fs-leak-detector.js +156 -0
  93. package/src/orchestrator/hu-sub-pipeline.js +197 -9
  94. package/src/orchestrator/integrations.js +88 -0
  95. package/src/orchestrator/post-loop-stages.js +35 -8
  96. package/src/orchestrator/preflight-checks.js +20 -7
  97. package/src/orchestrator/repair/repair-detector.js +117 -0
  98. package/src/orchestrator/repair/repair-runner.js +142 -0
  99. package/src/orchestrator/reviewer-fallback.js +1 -1
  100. package/src/orchestrator/solomon-escalation.js +3 -2
  101. package/src/orchestrator/solomon-rules.js +7 -3
  102. package/src/orchestrator/stages/architect-stage.js +12 -11
  103. package/src/orchestrator/stages/coder-stage.js +56 -7
  104. package/src/orchestrator/stages/hu-reviewer-phases/detect-and-apply-splits.js +127 -0
  105. package/src/orchestrator/stages/hu-reviewer-stage.js +6 -79
  106. package/src/orchestrator/stages/planner-stage.js +1 -1
  107. package/src/orchestrator/stages/research-stage.js +1 -1
  108. package/src/orchestrator/stages/reviewer-stage.js +7 -7
  109. package/src/orchestrator/stages/sonar-stage.js +94 -10
  110. package/src/orchestrator/stages/stage-classes.js +86 -0
  111. package/src/orchestrator/stages/stage-executor.js +1 -1
  112. package/src/orchestrator/stages/triage-stage.js +1 -1
  113. package/src/orchestrator/standby.js +3 -2
  114. package/src/orchestrator/verification-gate.js +20 -12
  115. package/src/plan/adr-generator.js +170 -0
  116. package/src/plan/adr-loader.js +60 -0
  117. package/src/plan/plan-executor.js +19 -1
  118. package/src/plan/plan-hu-ops.js +170 -1
  119. package/src/plan/plan-reviewer.js +199 -0
  120. package/src/plan/plan-schema.js +69 -1
  121. package/src/plan/plan-store.js +40 -3
  122. package/src/plan/spec-detector.js +61 -0
  123. package/src/plan/tests-synthesizer.js +116 -0
  124. package/src/planning-game/client.js +3 -14
  125. package/src/planning-game/decomposition.js +1 -1
  126. package/src/planning-game/pipeline-adapter.js +36 -5
  127. package/src/prompts/coder.js +172 -1
  128. package/src/prompts/hu-reviewer.js +0 -1
  129. package/src/prompts/planner.js +81 -4
  130. package/src/review/diff-generator.js +35 -22
  131. package/src/review/snapshot-diff.js +1 -1
  132. package/src/roles/coder-role.js +25 -3
  133. package/src/roles/planner-role.js +42 -1
  134. package/src/roles/repairer-role.js +181 -0
  135. package/src/roles/tester-role.js +70 -4
  136. package/src/session/mutators.js +304 -0
  137. package/src/session/runtime-overrides.js +59 -0
  138. package/src/{session-store.js → session/store.js} +2 -2
  139. package/src/skills/semantic-detector.js +10 -5
  140. package/src/skills/skill-detector.js +1 -1
  141. package/src/sonar/cloud-scanner.js +0 -1
  142. package/src/sonar/credentials.js +14 -6
  143. package/src/sonar/discovery.js +123 -0
  144. package/src/sonar/manager.js +18 -0
  145. package/src/sync/detect-drift.js +113 -0
  146. package/src/tracker-bootstrap.js +44 -0
  147. package/src/utils/agent-detect.js +15 -11
  148. package/src/utils/board-prompt-bridge.js +109 -0
  149. package/src/utils/cli-progress.js +161 -0
  150. package/src/utils/cwd-confirm.js +39 -0
  151. package/src/utils/derive-project-name-from-cwd.js +119 -0
  152. package/src/utils/garbage-collector.js +396 -0
  153. package/src/utils/git.js +16 -4
  154. package/src/utils/locale.js +1 -1
  155. package/src/utils/paths.js +21 -1
  156. package/src/utils/process.js +1 -1
  157. package/src/utils/prompt-project-name.js +35 -0
  158. package/src/utils/run-context.js +75 -0
  159. package/src/utils/run-log.js +12 -0
  160. package/src/utils/task-file.js +1 -1
  161. package/src/utils/telemetry.js +13 -1
  162. package/src/webperf/devtools-detect.js +1 -1
  163. package/templates/coder-rules.md +13 -0
  164. package/templates/roles/coder.md +34 -0
  165. package/templates/roles/repairer.md +94 -0
@@ -0,0 +1,159 @@
1
+ /**
2
+ * Role provider/model resolution + config validation.
3
+ *
4
+ * `resolveRole(config, role)` returns `{ provider, model }` for a role
5
+ * given the fully-overridden config, respecting legacy single-provider
6
+ * shortcuts (`coder: "claude"`) and per-role overrides
7
+ * (`roles.coder.provider`). Handles model compatibility (gemini models
8
+ * are dropped from a claude provider, etc.) so the agent subprocess
9
+ * never sees an incompatible `--model` flag.
10
+ *
11
+ * `validateConfig(config, commandName)` is the run-time guard called by
12
+ * every CLI command before spawning agents: it asserts every required
13
+ * role has a provider, otherwise throws with an actionable message
14
+ * ("Set 'roles.planner.provider' or pass '--planner <name>'").
15
+ *
16
+ * Extracted from `src/config.js` in TSK-0332 (Oleada 2 of the v2.7.4
17
+ * audit refactor). Public API: `resolveRole`, `validateConfig`. The
18
+ * inference helpers are internal.
19
+ */
20
+
21
+ /**
22
+ * Check if a model string is compatible with an agent provider.
23
+ * Only returns false when the model clearly belongs to a DIFFERENT provider.
24
+ * Returns true if we can't determine or if the model is ambiguous.
25
+ */
26
+ const AGENT_MODEL_SIGNATURES = {
27
+ claude: ["claude", "sonnet", "opus", "haiku"],
28
+ codex: ["o4-", "o3-", "gpt-", "codex"],
29
+ gemini: ["gemini", "flash-"]
30
+ };
31
+
32
+ function isModelCompatible(agent, model) {
33
+ if (!model || !agent) return true;
34
+ const lower = model.toLowerCase();
35
+
36
+ // Check if model clearly belongs to a different provider
37
+ for (const [provider, signatures] of Object.entries(AGENT_MODEL_SIGNATURES)) {
38
+ if (provider === agent) continue;
39
+ if (signatures.some(s => lower.includes(s))) {
40
+ // Model belongs to a different provider — incompatible
41
+ return false;
42
+ }
43
+ }
44
+
45
+ // Model doesn't clearly belong to any other provider — allow it
46
+ return true;
47
+ }
48
+
49
+ // Roles that inherit provider/model from the coder when not explicitly configured
50
+ const CODER_INHERITED_ROLES = new Set([
51
+ "planner", "refactorer", "solomon", "researcher", "tester", "security",
52
+ "impeccable", "triage", "discover", "architect", "audit", "hu_reviewer", "hu-reviewer"
53
+ ]);
54
+
55
+ function resolveProvider(roleConfig, role, roles, legacyCoder, legacyReviewer) {
56
+ if (roleConfig.provider) return roleConfig.provider;
57
+
58
+ // If model has "provider/model" format (e.g. "gemini/pro"), extract the provider
59
+ if (roleConfig.model && roleConfig.model.includes("/")) {
60
+ const inferredProvider = roleConfig.model.split("/")[0].toLowerCase();
61
+ if (AGENT_MODEL_SIGNATURES[inferredProvider]) return inferredProvider;
62
+ }
63
+
64
+ if (role === "coder") return legacyCoder;
65
+ if (role === "reviewer") return legacyReviewer;
66
+ if (CODER_INHERITED_ROLES.has(role)) return roles.coder?.provider || legacyCoder;
67
+ return null;
68
+ }
69
+
70
+ function resolveModel(roleConfig, role, config) {
71
+ if (roleConfig.model) {
72
+ // Strip "provider/" prefix from models like "gemini/pro" → "pro"
73
+ const model = roleConfig.model.includes("/")
74
+ ? roleConfig.model.split("/").slice(1).join("/")
75
+ : roleConfig.model;
76
+ return { model, inherited: false };
77
+ }
78
+ if (role === "coder") return { model: config?.coder_options?.model ?? null, inherited: false };
79
+ if (role === "reviewer") return { model: config?.reviewer_options?.model ?? null, inherited: false };
80
+ if (CODER_INHERITED_ROLES.has(role)) {
81
+ const model = config?.coder_options?.model ?? null;
82
+ return { model, inherited: !!model };
83
+ }
84
+ return { model: null, inherited: false };
85
+ }
86
+
87
+ export function resolveRole(config, role) {
88
+ const roles = config?.roles || {};
89
+ const roleConfig = roles[role] || {};
90
+ const legacyCoder = config?.coder || null;
91
+ const legacyReviewer = config?.reviewer || null;
92
+
93
+ const provider = resolveProvider(roleConfig, role, roles, legacyCoder, legacyReviewer);
94
+ let { model } = resolveModel(roleConfig, role, config);
95
+
96
+ // Drop model if incompatible with the resolved provider (inherited or explicit)
97
+ if (provider && model && !isModelCompatible(provider, model)) {
98
+ model = null;
99
+ }
100
+
101
+ return { provider, model };
102
+ }
103
+
104
+ // Pipeline roles checked when commandName is "run": [pipelineKey, roleName]
105
+ const RUN_PIPELINE_ROLES = [
106
+ ["reviewer", "reviewer"], ["triage", "triage"], ["planner", "planner"],
107
+ ["refactorer", "refactorer"], ["researcher", "researcher"],
108
+ ["tester", "tester"], ["security", "security"], ["impeccable", "impeccable"]
109
+ ];
110
+
111
+ // Direct command-to-role mapping for non-"run" commands
112
+ const COMMAND_ROLE_MAP = {
113
+ discover: ["discover"],
114
+ plan: ["planner"],
115
+ code: ["coder"],
116
+ review: ["reviewer"]
117
+ };
118
+
119
+ function requiredRolesFor(commandName, config) {
120
+ if (commandName !== "run") {
121
+ return COMMAND_ROLE_MAP[commandName] || [];
122
+ }
123
+ const required = ["coder"];
124
+ for (const [pipelineKey, roleName] of RUN_PIPELINE_ROLES) {
125
+ const pipelineEntry = config?.pipeline?.[pipelineKey];
126
+ // reviewer defaults to enabled (only excluded if explicitly false)
127
+ const isEnabled = pipelineKey === "reviewer"
128
+ ? pipelineEntry?.enabled !== false
129
+ : Boolean(pipelineEntry?.enabled);
130
+ if (isEnabled) required.push(roleName);
131
+ }
132
+ return required;
133
+ }
134
+
135
+ export function validateConfig(config, commandName = "run") {
136
+ const errors = [];
137
+ if (!new Set(["paranoid", "strict", "standard", "relaxed", "custom"]).has(config.review_mode)) {
138
+ errors.push(`Invalid review_mode: ${config.review_mode}`);
139
+ }
140
+ if (!new Set(["tdd", "standard"]).has(config.development?.methodology)) {
141
+ errors.push(`Invalid development.methodology: ${config.development?.methodology}`);
142
+ }
143
+
144
+ const requiredRoles = requiredRolesFor(commandName, config);
145
+ for (const role of requiredRoles) {
146
+ const { provider } = resolveRole(config, role);
147
+ if (!provider) {
148
+ errors.push(
149
+ `Missing provider for required role '${role}'. Set 'roles.${role}.provider' or pass '--${role} <name>'`
150
+ );
151
+ }
152
+ }
153
+
154
+ if (errors.length > 0) {
155
+ throw new Error(errors.join("\n"));
156
+ }
157
+
158
+ return config;
159
+ }
@@ -0,0 +1,79 @@
1
+ /**
2
+ * Test-harness-visible config knobs — resolved once per run, then read
3
+ * everywhere via `config.testHarness.*`.
4
+ *
5
+ * Pre-v2.7.5 these defaults were read from `globalThis.__KJ_*` directly
6
+ * in production code paths — `flow-runner.js` read
7
+ * `globalThis.__KJ_DEFAULT_BRAIN_DECISOR`, `preflight-checks.js` read
8
+ * `globalThis.__KJ_DISABLE_SONAR_STAGE`, etc. The audit flagged this as
9
+ * spooky-action-at-a-distance: a stray test-harness leftover could flip
10
+ * a production default, and tests did not exercise the same path
11
+ * production users would.
12
+ *
13
+ * TSK-0334 isolates the globalThis lookup to a single site (this module,
14
+ * run during config load) and exposes the resolved knobs under
15
+ * `config.testHarness`. Production code reads only from `config.*`,
16
+ * never from `globalThis`. Tests can set either `globalThis.__KJ_*`
17
+ * (back-compat for the ~10 existing test files that already do) or pass
18
+ * `config.testHarness = { ... }` explicitly.
19
+ *
20
+ * The `tests/architecture/no-globalThis-kj.test.js` invariant fails CI
21
+ * if a `globalThis.__KJ_` read reappears anywhere under `src/` outside
22
+ * this module.
23
+ *
24
+ * Resolution order (first defined wins):
25
+ * 1. `config.testHarness.<key>` (explicit test or user config)
26
+ * 2. `globalThis.__KJ_<MACRO>` (existing test-harness global, back-compat)
27
+ * 3. Documented default (second column below)
28
+ *
29
+ * | config.testHarness key | globalThis key | prod default |
30
+ * |--------------------------------|----------------------------------|--------------|
31
+ * | disableSonarStage | __KJ_DISABLE_SONAR_STAGE | false |
32
+ * | defaultSkillsMode | __KJ_DEFAULT_SKILLS_MODE | "auto" |
33
+ * | defaultPreflightExtended | __KJ_DEFAULT_PREFLIGHT_EXTENDED | true |
34
+ * | defaultBrainDecisor | __KJ_DEFAULT_BRAIN_DECISOR | true |
35
+ * | defaultAddyosmaniEnabled | __KJ_DEFAULT_ADDYOSMANI_ENABLED | true |
36
+ */
37
+
38
+ const GLOBAL_KEYS = {
39
+ disableSonarStage: "__KJ_DISABLE_SONAR_STAGE",
40
+ defaultSkillsMode: "__KJ_DEFAULT_SKILLS_MODE",
41
+ defaultPreflightExtended: "__KJ_DEFAULT_PREFLIGHT_EXTENDED",
42
+ defaultBrainDecisor: "__KJ_DEFAULT_BRAIN_DECISOR",
43
+ defaultAddyosmaniEnabled: "__KJ_DEFAULT_ADDYOSMANI_ENABLED",
44
+ };
45
+
46
+ const PROD_DEFAULTS = Object.freeze({
47
+ disableSonarStage: false,
48
+ defaultSkillsMode: "auto",
49
+ defaultPreflightExtended: true,
50
+ defaultBrainDecisor: true,
51
+ defaultAddyosmaniEnabled: true,
52
+ });
53
+
54
+ /**
55
+ * Resolve the testHarness slice of config. Safe to call multiple times;
56
+ * each call re-reads globalThis so test-harness overrides applied after
57
+ * config load still take effect on the next resolution.
58
+ *
59
+ * @param {Partial<typeof PROD_DEFAULTS>} [explicit] - typically `config.testHarness` passed in
60
+ * @returns {Readonly<typeof PROD_DEFAULTS>}
61
+ */
62
+ export function resolveTestHarness(explicit = {}) {
63
+ const out = {};
64
+ for (const [key, globalName] of Object.entries(GLOBAL_KEYS)) {
65
+ if (explicit && Object.prototype.hasOwnProperty.call(explicit, key) && explicit[key] !== undefined) {
66
+ out[key] = explicit[key];
67
+ continue;
68
+ }
69
+ const g = globalThis[globalName];
70
+ if (g !== undefined) {
71
+ out[key] = g;
72
+ continue;
73
+ }
74
+ out[key] = PROD_DEFAULTS[key];
75
+ }
76
+ return Object.freeze(out);
77
+ }
78
+
79
+ export { PROD_DEFAULTS, GLOBAL_KEYS };