@polderlabs/bizar 6.2.2 → 6.2.4

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.
@@ -73,7 +73,7 @@ describe("ClineRuntime.startSession — execution plumbing", () => {
73
73
  };
74
74
  const runtime = new ClineRuntime({
75
75
  logger: stubLogger(),
76
- defaultMaxConsecutiveMistakes: 6,
76
+ defaultMaxConsecutiveMistakes: 10,
77
77
  defaultOnConsecutiveMistakeLimitReached: recovery,
78
78
  });
79
79
  (runtime as unknown as { core: unknown }).core = makeFakeCore();
@@ -84,16 +84,57 @@ describe("ClineRuntime.startSession — execution plumbing", () => {
84
84
  prompt: "go",
85
85
  });
86
86
  const cfg = capturedConfig[0]!.config;
87
- expect(cfg.execution).toEqual({ maxConsecutiveMistakes: 6 });
87
+ expect(cfg.execution).toEqual({ maxConsecutiveMistakes: 10 });
88
88
  expect(typeof cfg.onConsecutiveMistakeLimitReached).toBe("function");
89
89
  // Invoke the wire-through function and confirm it IS the recovery closure.
90
90
  const fn = cfg.onConsecutiveMistakeLimitReached as (ctx: ConsecutiveMistakeLimitContext) => unknown;
91
- fn({ iteration: 1, consecutiveMistakes: 3, maxConsecutiveMistakes: 6, reason: "invalid_tool_call" });
91
+ fn({ iteration: 1, consecutiveMistakes: 3, maxConsecutiveMistakes: 10, reason: "invalid_tool_call" });
92
92
  expect(recoveryCalls.length).toBe(1);
93
93
  expect(recoveryCalls[0]!.reason).toBe("invalid_tool_call");
94
94
  });
95
95
 
96
- test("caller-supplied execution.maxConsecutiveMistakes wins over default", async () => {
96
+ test("v6.2.4 plugin default is a FLOOR; CLI default of 3 is bumped to plugin default", async () => {
97
+ // This is the v6.2.0 → v6.2.4 regression test. Previously the
98
+ // caller's maxConsecutiveMistakes (e.g. the Cline CLI's --retries
99
+ // default of 3) would silently override the plugin's higher
100
+ // default. Now we use Math.max so the plugin's 10 wins.
101
+ const runtime = new ClineRuntime({
102
+ logger: stubLogger(),
103
+ defaultMaxConsecutiveMistakes: 10,
104
+ });
105
+ (runtime as unknown as { core: unknown }).core = makeFakeCore();
106
+ await runtime.startSession({
107
+ providerId: "anthropic",
108
+ modelId: "claude-sonnet-4-6",
109
+ workspaceRoot: "/tmp",
110
+ prompt: "go",
111
+ execution: { maxConsecutiveMistakes: 3, reminderAfterIterations: 8 },
112
+ });
113
+ const cfg = capturedConfig[0]!.config;
114
+ expect(cfg.execution?.maxConsecutiveMistakes).toBe(10,
115
+ "plugin default must win over CLI default of 3 (was a v6.0 regression)");
116
+ expect(cfg.execution?.reminderAfterIterations).toBe(8, "other caller fields pass through");
117
+ });
118
+
119
+ test("caller-supplied HIGHER execution.maxConsecutiveMistakes still wins", async () => {
120
+ const runtime = new ClineRuntime({
121
+ logger: stubLogger(),
122
+ defaultMaxConsecutiveMistakes: 10,
123
+ });
124
+ (runtime as unknown as { core: unknown }).core = makeFakeCore();
125
+ await runtime.startSession({
126
+ providerId: "anthropic",
127
+ modelId: "claude-sonnet-4-6",
128
+ workspaceRoot: "/tmp",
129
+ prompt: "go",
130
+ execution: { maxConsecutiveMistakes: 20, reminderAfterIterations: 8 },
131
+ });
132
+ const cfg = capturedConfig[0]!.config;
133
+ expect(cfg.execution?.maxConsecutiveMistakes).toBe(20,
134
+ "user can still raise the limit with --retries 20");
135
+ });
136
+
137
+ test("caller-supplied execution.maxConsecutiveMistakes wins over default (legacy behavior)", async () => {
97
138
  const runtime = new ClineRuntime({
98
139
  logger: stubLogger(),
99
140
  defaultMaxConsecutiveMistakes: 4,
@@ -180,7 +221,7 @@ describe("ClineRuntime.startSession — agent teams plumbing (v6.2.0)", () => {
180
221
  expect(cfg.enableAgentTeams).toBe(true);
181
222
  });
182
223
 
183
- test("enableTools is true, enableSpawnAgent is false", async () => {
224
+ test("enableTools is true, enableSpawnAgent is true (subagents + task tool)", async () => {
184
225
  const runtime = new ClineRuntime({ logger: stubLogger() });
185
226
  (runtime as unknown as { core: unknown }).core = makeFakeCore();
186
227
  await runtime.startSession({
@@ -191,7 +232,8 @@ describe("ClineRuntime.startSession — agent teams plumbing (v6.2.0)", () => {
191
232
  });
192
233
  const cfg = capturedConfig[0]!.config;
193
234
  expect(cfg.enableTools).toBe(true);
194
- expect(cfg.enableSpawnAgent).toBe(false);
235
+ expect(cfg.enableSpawnAgent).toBe(true,
236
+ "enableSpawnAgent must be true so Odin can use the `task` tool and `use_subagents`");
195
237
  });
196
238
 
197
239
  test("all three boolean flags survive even when caller passes no opts", async () => {
@@ -206,7 +248,27 @@ describe("ClineRuntime.startSession — agent teams plumbing (v6.2.0)", () => {
206
248
  const cfg = capturedConfig[0]!.config;
207
249
  expect(cfg.enableAgentTeams).toBe(true);
208
250
  expect(cfg.enableTools).toBe(true);
209
- expect(cfg.enableSpawnAgent).toBe(false);
251
+ expect(cfg.enableSpawnAgent).toBe(true,
252
+ "v6.2.3 — subagents + task tool must be available even without caller opts");
253
+ });
254
+ });
255
+
256
+ describe("ClineRuntime.startSession — subagents plumbing (v6.2.3)", () => {
257
+ test("enableSpawnAgent regression: must NEVER be false (v6.2.3 fix)", async () => {
258
+ // v6.2.3 flipped enableSpawnAgent from false → true. Before this
259
+ // change, Odin could not use the `task` tool or `use_subagents`,
260
+ // which silently broke subagent delegation. This test pins the fix.
261
+ const runtime = new ClineRuntime({ logger: stubLogger() });
262
+ (runtime as unknown as { core: unknown }).core = makeFakeCore();
263
+ await runtime.startSession({
264
+ providerId: "anthropic",
265
+ modelId: "claude-sonnet-4-6",
266
+ workspaceRoot: "/tmp",
267
+ prompt: "go",
268
+ });
269
+ const cfg = capturedConfig[0]!.config;
270
+ expect(cfg.enableSpawnAgent).not.toBe(false);
271
+ expect(cfg.enableSpawnAgent).toBe(true);
210
272
  });
211
273
  });
212
274
 
@@ -278,16 +278,16 @@ describe("readEnvFlags", () => {
278
278
  // ── clineruntimeMaxConsecutiveMistakes ───────────────────────────────────────
279
279
 
280
280
  describe("clineruntimeMaxConsecutiveMistakes (v0.3.1)", () => {
281
- test("default is 6 when neither raw nor env is provided", () => {
281
+ test("default is 10 when neither raw nor env is provided (v6.2.4)", () => {
282
282
  delete process.env.BIZAR_MAX_CONSECUTIVE_MISTAKES;
283
283
  const { options } = normalizeOptions({});
284
- expect(options.clineruntimeMaxConsecutiveMistakes).toBe(6);
284
+ expect(options.clineruntimeMaxConsecutiveMistakes).toBe(10);
285
285
  });
286
286
 
287
287
  test("raw value is honored when in range", () => {
288
288
  delete process.env.BIZAR_MAX_CONSECUTIVE_MISTAKES;
289
- const { options, notes } = normalizeOptions({ clineruntimeMaxConsecutiveMistakes: 10 });
290
- expect(options.clineruntimeMaxConsecutiveMistakes).toBe(10);
289
+ const { options, notes } = normalizeOptions({ clineruntimeMaxConsecutiveMistakes: 15 });
290
+ expect(options.clineruntimeMaxConsecutiveMistakes).toBe(15);
291
291
  expect(notes.some((n) => n.includes("clineruntimeMaxConsecutiveMistakes"))).toBe(false);
292
292
  });
293
293
 
@@ -321,9 +321,9 @@ describe("clineruntimeMaxConsecutiveMistakes (v0.3.1)", () => {
321
321
  delete process.env.BIZAR_MAX_CONSECUTIVE_MISTAKES;
322
322
  });
323
323
 
324
- test("non-numeric raw falls back to default", () => {
324
+ test("non-numeric raw falls back to default (10)", () => {
325
325
  delete process.env.BIZAR_MAX_CONSECUTIVE_MISTAKES;
326
326
  const { options } = normalizeOptions({ clineruntimeMaxConsecutiveMistakes: "not-a-number" });
327
- expect(options.clineruntimeMaxConsecutiveMistakes).toBe(6);
327
+ expect(options.clineruntimeMaxConsecutiveMistakes).toBe(10);
328
328
  });
329
329
  });
@@ -125,7 +125,7 @@ if (!existsSync(PLUGIN_PATH)) {
125
125
  }
126
126
  record('plugin entry resolves', true, PLUGIN_PATH);
127
127
 
128
- // ── 1. Verify enableAgentTeams in source (the v6.2.0 fix) ──────
128
+ // ── 1. Verify enableAgentTeams + enableSpawnAgent in source (v6.2.0 / v6.2.3) ───────
129
129
  try {
130
130
  const { readFileSync } = await import('node:fs');
131
131
  const srcPath = join(REPO_ROOT, 'plugins', 'bizar', 'src', 'clineruntime.ts');
@@ -133,10 +133,17 @@ try {
133
133
  record('clineruntime.ts source present', false, `${srcPath} missing`);
134
134
  } else {
135
135
  const text = readFileSync(srcPath, 'utf8');
136
- if (/enableAgentTeams:\s*true/.test(text)) {
137
- record('enableAgentTeams: true in clineruntime.ts', true, 'team-spawn tool will work');
136
+ const teamsOk = /enableAgentTeams:\s*true/.test(text);
137
+ const spawnOk = /enableSpawnAgent:\s*true/.test(text);
138
+ if (teamsOk && spawnOk) {
139
+ record('enableAgentTeams + enableSpawnAgent: true in clineruntime.ts', true,
140
+ '/team and /subagent (use_subagents + task tool) both work');
138
141
  } else {
139
- record('enableAgentTeams: true in clineruntime.ts', false, 'REGRESSION: /team will not function');
142
+ const missing = [];
143
+ if (!teamsOk) missing.push('enableAgentTeams');
144
+ if (!spawnOk) missing.push('enableSpawnAgent');
145
+ record('enableAgentTeams + enableSpawnAgent: true in clineruntime.ts', false,
146
+ `REGRESSION: missing ${missing.join(', ')} — /team and subagents will not function`);
140
147
  }
141
148
  }
142
149
  } catch (err) {
@@ -336,6 +343,28 @@ try {
336
343
  record('plugin hooks dir scannable', false, err.message);
337
344
  }
338
345
 
346
+ // ── 9.5. Verify all 14 agents reference the shared docs (v6.2.4) ─────
347
+ // Catches drift: an agent that doesn't reference AGENT_BASELINE.md or
348
+ // CLINE_TOOLS.md won't get the always-on rules at runtime.
349
+ try {
350
+ const { spawnSync } = await import('node:child_process');
351
+ const r = spawnSync('node', ['scripts/check-agents.mjs'], {
352
+ encoding: 'utf8',
353
+ cwd: REPO_ROOT,
354
+ timeout: 10000,
355
+ });
356
+ if (r.status === 0) {
357
+ // Extract the count from the output
358
+ const m = /All (\d+) agents/.exec(r.stdout || '');
359
+ const n = m ? m[1] : '?';
360
+ record('all 14 agents reference AGENT_BASELINE + CLINE_TOOLS', true, `${n} agents OK`);
361
+ } else {
362
+ record('all 14 agents reference AGENT_BASELINE + CLINE_TOOLS', false, (r.stdout || r.stderr || '').trim().split('\n').slice(-3).join(' | '));
363
+ }
364
+ } catch (err) {
365
+ record('all 14 agents reference AGENT_BASELINE + CLINE_TOOLS', false, err.message);
366
+ }
367
+
339
368
  // ── 10. Verify cline CLI is on PATH and recent enough ──────────
340
369
  try {
341
370
  const { spawnSync } = await import('node:child_process');
@@ -378,6 +407,52 @@ try {
378
407
  record('bizar validate command present', false, err.message);
379
408
  }
380
409
 
410
+ // ── 12.6. Verify Cline CLI integration (v6.2.3) ─────────────────
411
+ // v6.2.3 — pass-through wrappers for Cline CLI commands + sample.
412
+ try {
413
+ const { existsSync } = await import('node:fs');
414
+ const clineCmd = join(REPO_ROOT, 'cli', 'commands', 'cline-cmd.mjs');
415
+ const rcaCmd = join(REPO_ROOT, 'cli', 'commands', 'rca.mjs');
416
+ const rcaTest = join(REPO_ROOT, 'cli', 'commands', 'rca.test.mjs');
417
+ const setupProviderCmd = join(REPO_ROOT, 'cli', 'commands', 'setup-provider.mjs');
418
+ const setupProviderTest = join(REPO_ROOT, 'cli', 'commands', 'setup-provider.test.mjs');
419
+ if (existsSync(clineCmd)) {
420
+ const text = (await import('node:fs')).readFileSync(clineCmd, 'utf8');
421
+ const hasConfig = /runClineConfig\b/.test(text);
422
+ const hasHistory = /runClineHistory\b/.test(text);
423
+ const hasHub = /runClineHub\b/.test(text);
424
+ const hasHook = /runClineHook\b/.test(text);
425
+ const hasTeam = /runClineTeam\b/.test(text);
426
+ const hasSubagent = /runClineSubagent\b/.test(text);
427
+ if (hasConfig && hasHistory && hasHub && hasHook && hasTeam && hasSubagent) {
428
+ record('bizar cline-cmd wrappers present', true, 'config, history, hub, hook, team, subagent');
429
+ } else {
430
+ const missing = [];
431
+ if (!hasConfig) missing.push('config');
432
+ if (!hasHistory) missing.push('history');
433
+ if (!hasHub) missing.push('hub');
434
+ if (!hasHook) missing.push('hook');
435
+ if (!hasTeam) missing.push('team');
436
+ if (!hasSubagent) missing.push('subagent');
437
+ record('bizar cline-cmd wrappers present', false, `missing: ${missing.join(', ')}`);
438
+ }
439
+ } else {
440
+ record('bizar cline-cmd wrappers present', false, 'cli/commands/cline-cmd.mjs missing');
441
+ }
442
+ if (existsSync(rcaCmd) && existsSync(rcaTest)) {
443
+ record('bizar rca (GitHub Issue RCA sample) present', true, 'cli/commands/rca.{mjs,test.mjs}');
444
+ } else {
445
+ record('bizar rca (GitHub Issue RCA sample) present', false, 'cli/commands/rca.* missing');
446
+ }
447
+ if (existsSync(setupProviderCmd) && existsSync(setupProviderTest)) {
448
+ record('bizar setup-provider + auto-migrate present', true, 'cli/commands/setup-provider.{mjs,test.mjs}');
449
+ } else {
450
+ record('bizar setup-provider + auto-migrate present', false, 'cli/commands/setup-provider.* missing');
451
+ }
452
+ } catch (err) {
453
+ record('bizar cline-cmd integration present', false, err.message);
454
+ }
455
+
381
456
  // ── 13. Verify package.json version is consistent ──────────────
382
457
  try {
383
458
  const { readFileSync } = await import('node:fs');
@@ -0,0 +1,73 @@
1
+ /**
2
+ * scripts/check-agents.mjs
3
+ *
4
+ * v6.2.4 — Verifies every agent file references the right shared docs.
5
+ *
6
+ * Fails (exit 1) if any of the 14 Bizar agents is missing the
7
+ * AGENT_BASELINE or CLINE_TOOLS reference. This catches drift early —
8
+ * an agent that doesn't reference the baseline doesn't get the
9
+ * always-on rules at runtime.
10
+ */
11
+ import { readFileSync, readdirSync, statSync } from 'node:fs';
12
+ import { join } from 'node:path';
13
+ import { fileURLToPath } from 'node:url';
14
+
15
+ const __filename = fileURLToPath(import.meta.url);
16
+ const ROOT = join(__filename, '..', '..');
17
+
18
+ const AGENT_FILES = [
19
+ 'agent-browser.md',
20
+ 'baldr.md',
21
+ 'forseti.md',
22
+ 'frigg.md',
23
+ 'heimdall.md',
24
+ 'hermod.md',
25
+ 'mimir.md',
26
+ 'odin.md',
27
+ 'quick.md',
28
+ 'semble-search.md',
29
+ 'thor.md',
30
+ 'tyr.md',
31
+ 'vidarr.md',
32
+ 'vor.md',
33
+ ];
34
+
35
+ let failed = 0;
36
+ const rows = [];
37
+
38
+ for (const file of AGENT_FILES) {
39
+ const path = join(ROOT, 'config', 'agents', file);
40
+ let text = '';
41
+ try {
42
+ text = readFileSync(path, 'utf8');
43
+ } catch (err) {
44
+ rows.push([file, 'MISSING FILE', '']);
45
+ failed++;
46
+ continue;
47
+ }
48
+ const hasBaseline = /AGENT_BASELINE|agent-baseline/i.test(text);
49
+ const hasClineTools = /CLINE_TOOLS/i.test(text);
50
+ if (!hasBaseline && !hasClineTools) {
51
+ rows.push([file, 'NO REFERENCE', 'needs AGENT_BASELINE or CLINE_TOOLS in description']);
52
+ failed++;
53
+ } else {
54
+ const refs = [
55
+ hasBaseline ? 'AGENT_BASELINE' : null,
56
+ hasClineTools ? 'CLINE_TOOLS' : null,
57
+ ].filter(Boolean).join('+');
58
+ rows.push([file, 'OK', refs]);
59
+ }
60
+ }
61
+
62
+ console.log('\n Agent → Shared-Docs reference check (v6.2.4)\n');
63
+ console.log(' ' + 'agent'.padEnd(24) + 'status'.padEnd(14) + 'references');
64
+ console.log(' ' + '-'.repeat(60));
65
+ for (const [file, status, refs] of rows) {
66
+ console.log(' ' + file.padEnd(24) + status.padEnd(14) + refs);
67
+ }
68
+ console.log('');
69
+ if (failed > 0) {
70
+ console.log(` ✗ ${failed} agent(s) missing the AGENT_BASELINE/CLINE_TOOLS reference.\n`);
71
+ process.exit(1);
72
+ }
73
+ console.log(` ✓ All ${AGENT_FILES.length} agents reference the shared docs.\n`);