karajan-code 4.6.2 → 4.6.3

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": "4.6.2",
3
+ "version": "4.6.3",
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",
@@ -27,6 +27,13 @@ export function isPublishableNpm(projectDir) {
27
27
  }
28
28
  }
29
29
 
30
+ /** Lockfile-detected package manager (KJC-BUG-0131): npm ci kills pnpm/yarn CI. */
31
+ export function detectPackageManager(projectDir) {
32
+ if (existsSync(join(projectDir, "pnpm-lock.yaml"))) return "pnpm";
33
+ if (existsSync(join(projectDir, "yarn.lock"))) return "yarn";
34
+ return "npm";
35
+ }
36
+
30
37
  export function installWorkflows({
31
38
  projectDir = process.cwd(),
32
39
  language = null,
@@ -35,9 +42,10 @@ export function installWorkflows({
35
42
  dryRun = false,
36
43
  } = {}) {
37
44
  const dir = join(projectDir, WORKFLOWS_DIR);
38
- const quality = qualityWorkflowFor(language);
39
- const extras = extraWorkflowsFor({ profile, publishable: isPublishableNpm(projectDir) });
40
- const mut = mutation ? mutationWorkflowFor(language) : null;
45
+ const pm = detectPackageManager(projectDir);
46
+ const quality = qualityWorkflowFor(language, pm);
47
+ const extras = extraWorkflowsFor({ profile, publishable: isPublishableNpm(projectDir), pm });
48
+ const mut = mutation ? mutationWorkflowFor(language, pm) : null;
41
49
  const all = [...WORKFLOWS, ...(quality ? [quality] : []), ...extras, ...(mut ? [mut] : [])];
42
50
  const results = [];
43
51
  for (const wf of all) {
@@ -54,15 +54,37 @@ const header = (steps) =>
54
54
  ...steps,
55
55
  ].join("\n");
56
56
 
57
+ // Per-package-manager commands (KJC-BUG-0131, issue #1330): `npm ci` in a
58
+ // pnpm/yarn repo dies with EUSAGE (no package-lock.json). corepack ships with
59
+ // Node 22 and resolves the right pnpm/yarn from `packageManager` — no
60
+ // third-party action. pnpm passes flags AFTER the script name to the script
61
+ // itself, so --if-present must go BEFORE it; yarn has no --if-present at all,
62
+ // so lint runs through `npm run` (scripts don't touch the lockfile).
63
+ const PM_COMMANDS = {
64
+ npm: { setup: [], install: "npm ci", lint: "npm run -s lint --if-present", test: "npm test" },
65
+ pnpm: {
66
+ setup: [" - run: corepack enable"],
67
+ install: "pnpm install --frozen-lockfile",
68
+ lint: "pnpm run --if-present -s lint",
69
+ test: "pnpm test",
70
+ },
71
+ yarn: {
72
+ setup: [" - run: corepack enable"],
73
+ install: "yarn install --frozen-lockfile",
74
+ lint: "npm run -s lint --if-present",
75
+ test: "yarn test",
76
+ },
77
+ };
78
+
79
+ const nodeSetupSteps = (pm) => [
80
+ " - uses: actions/setup-node@v4",
81
+ " with:",
82
+ " node-version: 22",
83
+ ...(PM_COMMANDS[pm] || PM_COMMANDS.npm).setup,
84
+ ` - run: ${(PM_COMMANDS[pm] || PM_COMMANDS.npm).install}`,
85
+ ];
86
+
57
87
  const QUALITY_BY_LANGUAGE = {
58
- javascript: header([
59
- " - uses: actions/setup-node@v4",
60
- " with:",
61
- " node-version: 22",
62
- " - run: npm ci",
63
- " - run: npm run -s lint --if-present",
64
- " - run: npm test",
65
- ]),
66
88
  python: header([
67
89
  " - uses: actions/setup-python@v5",
68
90
  " with:",
@@ -87,11 +109,13 @@ const QUALITY_BY_LANGUAGE = {
87
109
  " - run: vendor/bin/phpunit",
88
110
  ]),
89
111
  };
90
- QUALITY_BY_LANGUAGE.typescript = QUALITY_BY_LANGUAGE.javascript;
91
-
92
112
  /** Stack-aware Quality workflow entry, or null for an unknown language. */
93
- export function qualityWorkflowFor(language) {
94
- const body = QUALITY_BY_LANGUAGE[language];
113
+ export function qualityWorkflowFor(language, pm = "npm") {
114
+ const c = PM_COMMANDS[pm] || PM_COMMANDS.npm;
115
+ const body =
116
+ language === "javascript" || language === "typescript"
117
+ ? header([...nodeSetupSteps(pm), ` - run: ${c.lint}`, ` - run: ${c.test}`])
118
+ : QUALITY_BY_LANGUAGE[language];
95
119
  return body ? { file: "kj-quality.yml", blockId: "wf-quality", body } : null;
96
120
  }
97
121
 
@@ -123,29 +147,29 @@ export const SHRINK_BUDGET_WORKFLOW = [
123
147
  ].join("\n");
124
148
 
125
149
  // Packs the tarball and installs it clean — only for publishable npm packages.
126
- export const PACK_SMOKE_WORKFLOW = [
127
- "name: Pack smoke",
128
- "on:",
129
- " pull_request:",
130
- " branches: [main]",
131
- "permissions:",
132
- " contents: read",
133
- "jobs:",
134
- " pack-smoke:",
135
- " runs-on: ubuntu-latest",
136
- " steps:",
137
- " - uses: actions/checkout@v4",
138
- " - uses: actions/setup-node@v4",
139
- " with:",
140
- " node-version: 22",
141
- " - run: npm ci",
142
- " - name: Pack and install the tarball clean",
143
- " run: |",
144
- " tgz=$(npm pack --silent)",
145
- " dir=$(mktemp -d)",
146
- ' npm install --prefix "$dir" "$PWD/$tgz" --no-audit --no-fund',
147
- ' echo "Tarball installs clean: $tgz"',
148
- ].join("\n");
150
+ // `npm pack`/`npm install <tarball>` need no lockfile, so only the dependency
151
+ // install step is package-manager-aware.
152
+ export const packSmokeWorkflow = (pm = "npm") =>
153
+ [
154
+ "name: Pack smoke",
155
+ "on:",
156
+ " pull_request:",
157
+ " branches: [main]",
158
+ "permissions:",
159
+ " contents: read",
160
+ "jobs:",
161
+ " pack-smoke:",
162
+ " runs-on: ubuntu-latest",
163
+ " steps:",
164
+ " - uses: actions/checkout@v4",
165
+ ...nodeSetupSteps(pm),
166
+ " - name: Pack and install the tarball clean",
167
+ " run: |",
168
+ " tgz=$(npm pack --silent)",
169
+ " dir=$(mktemp -d)",
170
+ ' npm install --prefix "$dir" "$PWD/$tgz" --no-audit --no-fund',
171
+ ' echo "Tarball installs clean: $tgz"',
172
+ ].join("\n");
149
173
 
150
174
  // Opt-in nightly/manual mutation audit (KJC-TSK-0589). NEVER a PR gate: it runs
151
175
  // on a weekly schedule + manual dispatch, and the mutate step is non-blocking
@@ -153,7 +177,7 @@ export const PACK_SMOKE_WORKFLOW = [
153
177
  // yields a JSON report (stryker/mutmut/infection) get a workflow — no silent
154
178
  // scaffold for tools that can't report yet.
155
179
  const MUTATION_TOOLCHAIN = {
156
- javascript: [" - run: npm ci"],
180
+ javascript: "node", // resolved per package manager in mutationWorkflowFor
157
181
  python: [
158
182
  " - uses: actions/setup-python@v5",
159
183
  " with:",
@@ -170,9 +194,11 @@ const MUTATION_TOOLCHAIN = {
170
194
  MUTATION_TOOLCHAIN.typescript = MUTATION_TOOLCHAIN.javascript;
171
195
 
172
196
  /** Opt-in mutation workflow entry, or null for a stack without a JSON report. */
173
- export function mutationWorkflowFor(language) {
174
- const toolchain = MUTATION_TOOLCHAIN[language];
175
- if (!toolchain) return null;
197
+ export function mutationWorkflowFor(language, pm = "npm") {
198
+ const entry = MUTATION_TOOLCHAIN[language];
199
+ if (!entry) return null;
200
+ const c = PM_COMMANDS[pm] || PM_COMMANDS.npm;
201
+ const toolchain = entry === "node" ? [...c.setup, ` - run: ${c.install}`] : entry;
176
202
  const body = [
177
203
  "name: Mutation (nightly)",
178
204
  "on:",
@@ -200,13 +226,13 @@ export function mutationWorkflowFor(language) {
200
226
  }
201
227
 
202
228
  /** Conditional workflows: shrink-budget on strict, pack-smoke when publishable. */
203
- export function extraWorkflowsFor({ profile = "standard", publishable = false } = {}) {
229
+ export function extraWorkflowsFor({ profile = "standard", publishable = false, pm = "npm" } = {}) {
204
230
  const extras = [];
205
231
  if (profile === "strict") {
206
232
  extras.push({ file: "kj-shrink-budget.yml", blockId: "wf-shrink", body: SHRINK_BUDGET_WORKFLOW });
207
233
  }
208
234
  if (publishable) {
209
- extras.push({ file: "kj-pack-smoke.yml", blockId: "wf-pack-smoke", body: PACK_SMOKE_WORKFLOW });
235
+ extras.push({ file: "kj-pack-smoke.yml", blockId: "wf-pack-smoke", body: packSmokeWorkflow(pm) });
210
236
  }
211
237
  return extras;
212
238
  }
@@ -17,9 +17,17 @@ export class CoderRole extends AgentRole {
17
17
  }
18
18
 
19
19
  createAgentInstance(provider) {
20
+ // KJC-BUG-0129 (issues #1301/#1303): running INSIDE an agent does not
21
+ // make it "the host executor" — delegating sent the full instruction
22
+ // prompt through elicitInput as a hanging board "question". A CLI
23
+ // provider spawns as a subprocess ALWAYS; host delegation is explicit
24
+ // opt-in (roles.coder.host_delegation: true) for MCP-only setups.
20
25
  if (this._askHost && isHostAgent(provider)) {
21
- this.logger.info(`Host-as-coder: delegating to host AI (skipping ${provider} subprocess)`);
22
- return new HostAgent(this.config, this.logger, { askHost: this._askHost });
26
+ if (this.config?.roles?.coder?.host_delegation === true) {
27
+ this.logger.info(`Host-as-coder: delegating to host AI (roles.coder.host_delegation, skipping ${provider} subprocess)`);
28
+ return new HostAgent(this.config, this.logger, { askHost: this._askHost });
29
+ }
30
+ this.logger.info(`Running inside ${provider} — spawning it as a subprocess anyway (set roles.coder.host_delegation: true to delegate to the host)`);
23
31
  }
24
32
  return this._createAgent(provider, this.config, this.logger);
25
33
  }