@polderlabs/bizar 10.6.0 → 10.7.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 (109) hide show
  1. package/bizar-dash/tests/memory-protocol-drift.test.mjs +18 -11
  2. package/cli/__tests__/feature-list-bridge.test.mjs +42 -42
  3. package/cli/agent-browser-up.sh +35 -35
  4. package/cli/agent-browser-update.mjs +42 -42
  5. package/cli/agent-browser-update.test.mjs +7 -7
  6. package/cli/audit.mjs +3 -3
  7. package/cli/bin.mjs +10 -10
  8. package/cli/bootstrap.mjs +1 -1
  9. package/cli/cli-commands-validation.test.mjs +2 -2
  10. package/cli/commands/claim.mjs +3 -3
  11. package/cli/commands/claude-cmd.mjs +3 -3
  12. package/cli/commands/eval.mjs +6 -6
  13. package/cli/commands/util.mjs +18 -18
  14. package/cli/commands/validate.mjs +10 -10
  15. package/cli/init.mjs +1 -1
  16. package/cli/install/postinstall.mjs +4 -4
  17. package/cli/plow-through.test.mjs +7 -6
  18. package/cli/prompts.mjs +11 -11
  19. package/cli/provision.mjs +4 -16
  20. package/config/skills/bizar/SKILL.md +1 -1
  21. package/config/skills/skillopt/SKILL.md +129 -0
  22. package/config/skills/thinking-archetypes/SKILL.md +90 -0
  23. package/config/skills/thinking-bayesian/SKILL.md +267 -0
  24. package/config/skills/thinking-bounded-rationality/SKILL.md +406 -0
  25. package/config/skills/thinking-circle-of-competence/SKILL.md +216 -0
  26. package/config/skills/thinking-cynefin/SKILL.md +70 -0
  27. package/config/skills/thinking-debiasing/SKILL.md +192 -0
  28. package/config/skills/thinking-dual-process/SKILL.md +282 -0
  29. package/config/skills/thinking-effectuation/SKILL.md +366 -0
  30. package/config/skills/thinking-feedback-loops/SKILL.md +464 -0
  31. package/config/skills/thinking-fermi-estimation/SKILL.md +263 -0
  32. package/config/skills/thinking-first-principles/SKILL.md +167 -0
  33. package/config/skills/thinking-five-whys-plus/SKILL.md +139 -0
  34. package/config/skills/thinking-inversion/SKILL.md +195 -0
  35. package/config/skills/thinking-jobs-to-be-done/SKILL.md +363 -0
  36. package/config/skills/thinking-kepner-tregoe/SKILL.md +154 -0
  37. package/config/skills/thinking-leverage-points/SKILL.md +390 -0
  38. package/config/skills/thinking-lindy-effect/SKILL.md +331 -0
  39. package/config/skills/thinking-map-territory/SKILL.md +111 -0
  40. package/config/skills/thinking-margin-of-safety/SKILL.md +330 -0
  41. package/config/skills/thinking-model-combination/SKILL.md +406 -0
  42. package/config/skills/thinking-model-router/SKILL.md +360 -0
  43. package/config/skills/thinking-model-selection/SKILL.md +341 -0
  44. package/config/skills/thinking-occams-razor/SKILL.md +129 -0
  45. package/config/skills/thinking-ooda/SKILL.md +127 -0
  46. package/config/skills/thinking-opportunity-cost/SKILL.md +360 -0
  47. package/config/skills/thinking-pre-mortem/SKILL.md +170 -0
  48. package/config/skills/thinking-probabilistic/SKILL.md +324 -0
  49. package/config/skills/thinking-red-team/SKILL.md +142 -0
  50. package/config/skills/thinking-regret-minimization/SKILL.md +335 -0
  51. package/config/skills/thinking-reversibility/SKILL.md +326 -0
  52. package/config/skills/thinking-scientific-method/SKILL.md +162 -0
  53. package/config/skills/thinking-second-order/SKILL.md +184 -0
  54. package/config/skills/thinking-socratic/SKILL.md +198 -0
  55. package/config/skills/thinking-steel-manning/SKILL.md +332 -0
  56. package/config/skills/thinking-systems/SKILL.md +238 -0
  57. package/config/skills/thinking-theory-of-constraints/SKILL.md +338 -0
  58. package/config/skills/thinking-thought-experiment/SKILL.md +354 -0
  59. package/config/skills/thinking-triz/SKILL.md +171 -0
  60. package/config/skills/thinking-via-negativa/SKILL.md +358 -0
  61. package/package.json +2 -3
  62. package/packages/sdk/dist/consensus/index.js +4 -4
  63. package/packages/sdk/dist/router/agent-model-registry.js +2 -2
  64. package/packages/sdk/dist/router/index.d.ts +3 -3
  65. package/packages/sdk/dist/router/index.js +4 -4
  66. package/packages/sdk/dist/router/q-learning-router.d.ts +2 -2
  67. package/packages/sdk/dist/router/q-learning-router.js +18 -18
  68. package/scripts/bh-full-e2e.mjs +10 -8
  69. package/scripts/check-agents.mjs +27 -24
  70. package/scripts/mirror-claude-md.sh +0 -0
  71. package/scripts/sync-skills-mirror.mjs +113 -0
  72. package/scripts/verify-thinking-skills.mjs +135 -0
  73. package/config/agents/_shared/AGENT_BASELINE.md +0 -155
  74. package/config/agents/_shared/CLINE_TOOLS.md +0 -398
  75. package/config/agents/_shared/SKILLS.md +0 -109
  76. package/config/agents/agent-browser.md +0 -90
  77. package/config/agents/baldr.md +0 -60
  78. package/config/agents/forseti.md +0 -60
  79. package/config/agents/frigg.md +0 -52
  80. package/config/agents/heimdall.md +0 -44
  81. package/config/agents/hermod.md +0 -64
  82. package/config/agents/mimir.md +0 -60
  83. package/config/agents/odin.md +0 -310
  84. package/config/agents/quick.md +0 -47
  85. package/config/agents/semble-search.md +0 -59
  86. package/config/agents/thor.md +0 -64
  87. package/config/agents/tyr.md +0 -67
  88. package/config/agents/vidarr.md +0 -65
  89. package/config/agents/vor.md +0 -62
  90. package/config/commands/audit.md +0 -5
  91. package/config/commands/bizar.md +0 -22
  92. package/config/commands/explain.md +0 -5
  93. package/config/commands/init.md +0 -26
  94. package/config/commands/learn.md +0 -5
  95. package/config/commands/plan.md +0 -30
  96. package/config/commands/plow-through.md +0 -47
  97. package/config/commands/pr-review.md +0 -5
  98. package/config/commands/setup-provider.md +0 -95
  99. package/config/commands/tailscale-serve.md +0 -97
  100. package/config/commands/team.md +0 -124
  101. package/config/commands/test.md +0 -60
  102. package/config/commands/validate.md +0 -66
  103. package/config/commands/visual-plan.md +0 -19
  104. package/config/hooks/PostToolUse +0 -66
  105. package/config/hooks/PreToolUse +0 -82
  106. package/config/hooks/README.md +0 -68
  107. package/config/hooks/TaskResume +0 -48
  108. package/config/hooks/TaskStart +0 -47
  109. package/config/hooks/UserPromptSubmit +0 -55
@@ -18,27 +18,34 @@ test('memory-protocol SKILL.md has MANDATORY framing at the top', () => {
18
18
  assert.match(head, /MANDATORY/, 'memory-protocol/SKILL.md must mark the protocol as MANDATORY in the first 10 lines');
19
19
  });
20
20
 
21
+ // F-107: AGENT_BASELINE.md is canonical at .claude/agents/_shared/.
22
+ // The legacy config/agents/_shared/ copy was removed (Cline-era).
21
23
  test('AGENT_BASELINE.md section 5 has MANDATORY marker', () => {
22
- const baseline = read('config/agents/_shared/AGENT_BASELINE.md');
24
+ const baseline = read('.claude/agents/_shared/AGENT_BASELINE.md');
23
25
  // Find section 5 header and the next 20 lines
24
26
  const section5Idx = baseline.indexOf('## 5.');
25
27
  assert.ok(section5Idx >= 0, 'AGENT_BASELINE.md must have a section 5');
26
28
  const section5Head = baseline.slice(section5Idx, section5Idx + 600);
27
- assert.match(section5Head, /MANDATORY/, 'AGENT_BASELINE.md section 5 must mark memory check as MANDATORY');
29
+ // Canonical uses "**Mandatory at session start.**" (mixed case, bold).
30
+ assert.match(section5Head, /[Mm]andatory/, 'AGENT_BASELINE.md section 5 must mark memory check as Mandatory');
28
31
  });
29
32
 
30
- test('AGENT_BASELINE.md section 13 has mandatory language', () => {
31
- const baseline = read('config/agents/_shared/AGENT_BASELINE.md');
32
- const section13Idx = baseline.indexOf('## 13.');
33
- assert.ok(section13Idx >= 0, 'AGENT_BASELINE.md must have a section 13');
34
- const section13Head = baseline.slice(section13Idx, section13Idx + 3000);
35
- // Either the ⚠️ ENFORCED marker we just added, or the existing "mandatory, not optional" line
36
- const hasMandatory = /MANDATORY/.test(section13Head) || /mandatory, not optional/i.test(section13Head);
37
- assert.ok(hasMandatory, 'AGENT_BASELINE.md section 13 must contain MANDATORY framing or the existing "mandatory, not optional" language');
33
+ test('AGENT_BASELINE.md self-improvement section has mandatory language', () => {
34
+ const baseline = read('.claude/agents/_shared/AGENT_BASELINE.md');
35
+ // Canonical has section 12 (Brenda's Records Duty) — not 13.
36
+ // Legacy had a "## 13." stub that was removed during the Claude Code migration.
37
+ const section12Idx = baseline.indexOf('## 12.');
38
+ assert.ok(section12Idx >= 0, 'AGENT_BASELINE.md must have a section 12 (Brenda self-improvement duty)');
39
+ const section12Head = baseline.slice(section12Idx, section12Idx + 3000);
40
+ // Either the canonical "Brenda-only. After every implementation" framing
41
+ // or the legacy "mandatory, not optional" line. The point: enforce that
42
+ // self-improvement is a duty, not optional guidance.
43
+ const hasDuty = /Brenda-only/.test(section12Head) || /mandatory, not optional/i.test(section12Head);
44
+ assert.ok(hasDuty, 'AGENT_BASELINE.md section 12 must contain a duty/mandatory framing for Brenda self-improvement');
38
45
  });
39
46
 
40
47
  test('AGENT_BASELINE.md session-start bootstrap commands are present', () => {
41
- const baseline = read('config/agents/_shared/AGENT_BASELINE.md');
48
+ const baseline = read('.claude/agents/_shared/AGENT_BASELINE.md');
42
49
  // The actual commands agents must run
43
50
  assert.match(baseline, /bizar memory search/, 'AGENT_BASELINE.md must reference `bizar memory search`');
44
51
  assert.match(baseline, /bizar memory status/, 'AGENT_BASELINE.md must reference `bizar memory status`');
@@ -82,10 +82,10 @@ describe('claim()', () => {
82
82
  afterEach(() => { /* tmp cleaned in after() */ });
83
83
 
84
84
  test('first claim succeeds', () => {
85
- const result = br.claim('F-A', { type: 'human', id: 'odin', name: 'Odin' }, 'first-run');
85
+ const result = br.claim('F-A', { type: 'human', id: 'mike', name: 'Odin' }, 'first-run');
86
86
  assert.equal(result.ok, true);
87
87
  const f = result.feature;
88
- assert.equal(f.claimant.id, 'odin');
88
+ assert.equal(f.claimant.id, 'mike');
89
89
  assert.equal(f.claimStatus, 'claimed');
90
90
  assert.ok(f.claimedAt);
91
91
  assert.equal(f.claimReason, 'first-run');
@@ -94,9 +94,9 @@ describe('claim()', () => {
94
94
  });
95
95
 
96
96
  test('second claim rejected with ALREADY_CLAIMED', () => {
97
- br.claim('F-A', { type: 'human', id: 'odin', name: 'Odin' }, 'first-run');
97
+ br.claim('F-A', { type: 'human', id: 'mike', name: 'Odin' }, 'first-run');
98
98
  assert.throws(
99
- () => br.claim('F-A', { type: 'agent', id: 'tyr' }, 'second-run'),
99
+ () => br.claim('F-A', { type: 'agent', id: 'karen' }, 'second-run'),
100
100
  (err) => err instanceof ClaimError && err.code === 'ALREADY_CLAIMED',
101
101
  );
102
102
  });
@@ -116,10 +116,10 @@ describe('claim()', () => {
116
116
  });
117
117
 
118
118
  test('can re-claim a completed feature', () => {
119
- br.claim('F-A', { type: 'human', id: 'odin' }, '');
120
- br.transition('F-A', 'completed', { id: 'odin' }, '');
119
+ br.claim('F-A', { type: 'human', id: 'mike' }, '');
120
+ br.transition('F-A', 'completed', { id: 'mike' }, '');
121
121
  // released -> re-claimable
122
- const r2 = br.claim('F-A', { type: 'agent', id: 'tyr' }, 'second life');
122
+ const r2 = br.claim('F-A', { type: 'agent', id: 'karen' }, 'second life');
123
123
  assert.equal(r2.ok, true);
124
124
  });
125
125
  });
@@ -135,8 +135,8 @@ describe('release()', () => {
135
135
  });
136
136
 
137
137
  test('release after claim sets status to unclaimed', () => {
138
- br.claim('F-X', { type: 'human', id: 'odin' }, '');
139
- const result = br.release('F-X', { id: 'odin' }, 'done');
138
+ br.claim('F-X', { type: 'human', id: 'mike' }, '');
139
+ const result = br.release('F-X', { id: 'mike' }, 'done');
140
140
  assert.equal(result.ok, true);
141
141
  assert.equal(result.feature.claimStatus, 'unclaimed');
142
142
  assert.equal(result.feature.claimant, null);
@@ -145,15 +145,15 @@ describe('release()', () => {
145
145
 
146
146
  test('release without claim errors with NOT_CLAIMED', () => {
147
147
  assert.throws(
148
- () => br.release('F-X', { id: 'odin' }, ''),
148
+ () => br.release('F-X', { id: 'mike' }, ''),
149
149
  (err) => err instanceof ClaimError && err.code === 'NOT_CLAIMED',
150
150
  );
151
151
  });
152
152
 
153
153
  test('release with wrong claimant errors with WRONG_CLAIMANT', () => {
154
- br.claim('F-X', { type: 'human', id: 'odin' }, '');
154
+ br.claim('F-X', { type: 'human', id: 'mike' }, '');
155
155
  assert.throws(
156
- () => br.release('F-X', { id: 'tyr' }, ''),
156
+ () => br.release('F-X', { id: 'karen' }, ''),
157
157
  (err) => err instanceof ClaimError && err.code === 'WRONG_CLAIMANT',
158
158
  );
159
159
  });
@@ -170,15 +170,15 @@ describe('handoff()', () => {
170
170
  });
171
171
 
172
172
  test('handoff moves claimant', () => {
173
- br.claim('F-H', { type: 'human', id: 'odin' }, '');
173
+ br.claim('F-H', { type: 'human', id: 'mike' }, '');
174
174
  const result = br.handoff(
175
175
  'F-H',
176
- { type: 'human', id: 'odin' },
177
- { type: 'agent', id: 'tyr', name: 'Tyr' },
176
+ { type: 'human', id: 'mike' },
177
+ { type: 'agent', id: 'karen', name: 'Tyr' },
178
178
  'context finished',
179
179
  );
180
180
  assert.equal(result.ok, true);
181
- assert.equal(result.feature.claimant.id, 'tyr');
181
+ assert.equal(result.feature.claimant.id, 'karen');
182
182
  assert.equal(result.feature.claimStatus, 'claimed');
183
183
  // history: claim + handoff-initiated + handoff-accepted
184
184
  const events = result.feature.claimHistory.map((e) => e.event);
@@ -189,8 +189,8 @@ describe('handoff()', () => {
189
189
  assert.throws(
190
190
  () => br.handoff(
191
191
  'F-H',
192
- { type: 'human', id: 'odin' },
193
- { type: 'agent', id: 'tyr' },
192
+ { type: 'human', id: 'mike' },
193
+ { type: 'agent', id: 'karen' },
194
194
  '',
195
195
  ),
196
196
  (err) => err instanceof ClaimError && err.code === 'NOT_CLAIMED',
@@ -209,17 +209,17 @@ describe('steal()', () => {
209
209
  });
210
210
 
211
211
  test('steal moves the claim', () => {
212
- br.claim('F-S', { type: 'human', id: 'odin' }, '');
212
+ br.claim('F-S', { type: 'human', id: 'mike' }, '');
213
213
  for (const reason of STEAL_REASONS) {
214
214
  // Steal a fresh feature for each reason to avoid ALREADY_CLAIMED.
215
215
  const f = minimalFeature(`F-S-${reason}`);
216
216
  const fFile = writeFixture(tmp, `s-${reason}.json`, [f]);
217
217
  const subBridge = bridgeFactory(fFile);
218
218
  subBridge.claim(`F-S-${reason}`, { type: 'agent', id: 'previous' }, '');
219
- const result = subBridge.steal(`F-S-${reason}`, { type: 'agent', id: 'tyr' }, reason);
219
+ const result = subBridge.steal(`F-S-${reason}`, { type: 'agent', id: 'karen' }, reason);
220
220
  assert.equal(result.ok, true);
221
221
  assert.equal(result.previousClaimant.id, 'previous');
222
- assert.equal(result.feature.claimant.id, 'tyr');
222
+ assert.equal(result.feature.claimant.id, 'karen');
223
223
  assert.equal(result.stealReason, reason);
224
224
  assert.ok(
225
225
  result.feature.claimHistory.some((e) => e.event === 'steal' && e.reason === reason),
@@ -228,9 +228,9 @@ describe('steal()', () => {
228
228
  });
229
229
 
230
230
  test('steal rejects unknown reason', () => {
231
- br.claim('F-S', { type: 'human', id: 'odin' }, '');
231
+ br.claim('F-S', { type: 'human', id: 'mike' }, '');
232
232
  assert.throws(
233
- () => br.steal('F-S', { type: 'agent', id: 'tyr' }, 'bogus-reason'),
233
+ () => br.steal('F-S', { type: 'agent', id: 'karen' }, 'bogus-reason'),
234
234
  (err) => err instanceof ClaimError && err.code === 'UNKNOWN_STEAL_REASON',
235
235
  );
236
236
  });
@@ -247,30 +247,30 @@ describe('transition()', () => {
247
247
  });
248
248
 
249
249
  test('claim → active → paused → active → completed', () => {
250
- br.claim('F-T', { type: 'human', id: 'odin' }, '');
251
- br.transition('F-T', 'active', { id: 'odin' }, '');
250
+ br.claim('F-T', { type: 'human', id: 'mike' }, '');
251
+ br.transition('F-T', 'active', { id: 'mike' }, '');
252
252
  assert.equal(br.get('F-T').claimStatus, 'active');
253
- br.transition('F-T', 'paused', { id: 'odin' }, 'lunch');
253
+ br.transition('F-T', 'paused', { id: 'mike' }, 'lunch');
254
254
  assert.equal(br.get('F-T').claimStatus, 'paused');
255
- br.transition('F-T', 'active', { id: 'odin' }, '');
255
+ br.transition('F-T', 'active', { id: 'mike' }, '');
256
256
  assert.equal(br.get('F-T').claimStatus, 'active');
257
- br.transition('F-T', 'completed', { id: 'odin' }, '');
257
+ br.transition('F-T', 'completed', { id: 'mike' }, '');
258
258
  assert.equal(br.get('F-T').claimStatus, 'completed');
259
259
  });
260
260
 
261
261
  test('illegal transition rejected', () => {
262
- br.claim('F-T', { type: 'human', id: 'odin' }, '');
262
+ br.claim('F-T', { type: 'human', id: 'mike' }, '');
263
263
  // claimed → blocked is NOT in ALLOWED_TRANSITIONS['claimed']
264
264
  assert.throws(
265
- () => br.transition('F-T', 'blocked', { id: 'odin' }, ''),
265
+ () => br.transition('F-T', 'blocked', { id: 'mike' }, ''),
266
266
  (err) => err instanceof ClaimError && err.code === 'BAD_TRANSITION',
267
267
  );
268
268
  });
269
269
 
270
270
  test('unknown status rejected', () => {
271
- br.claim('F-T', { type: 'human', id: 'odin' }, '');
271
+ br.claim('F-T', { type: 'human', id: 'mike' }, '');
272
272
  assert.throws(
273
- () => br.transition('F-T', 'bogus', { id: 'odin' }, ''),
273
+ () => br.transition('F-T', 'bogus', { id: 'mike' }, ''),
274
274
  (err) => err instanceof ClaimError && err.code === 'UNKNOWN_STATUS',
275
275
  );
276
276
  });
@@ -292,12 +292,12 @@ describe('list()', () => {
292
292
  });
293
293
 
294
294
  test('claims 2, handoffs 1, steals 1 — list filters', () => {
295
- br.claim('F-1', { type: 'human', id: 'odin' }, '');
296
- br.claim('F-2', { type: 'agent', id: 'tyr' }, '');
297
- br.claim('F-3', { type: 'human', id: 'odin' }, '');
298
- br.handoff('F-3', { type: 'human', id: 'odin' }, { type: 'agent', id: 'tyr' }, '');
299
- br.claim('F-4', { type: 'human', id: 'mimir' }, '');
300
- br.steal('F-4', { type: 'agent', id: 'tyr' }, 'voluntary');
295
+ br.claim('F-1', { type: 'human', id: 'mike' }, '');
296
+ br.claim('F-2', { type: 'agent', id: 'karen' }, '');
297
+ br.claim('F-3', { type: 'human', id: 'mike' }, '');
298
+ br.handoff('F-3', { type: 'human', id: 'mike' }, { type: 'agent', id: 'karen' }, '');
299
+ br.claim('F-4', { type: 'human', id: 'greg' }, '');
300
+ br.steal('F-4', { type: 'agent', id: 'karen' }, 'voluntary');
301
301
 
302
302
  const all = br.list();
303
303
  assert.equal(all.length, 4);
@@ -310,8 +310,8 @@ describe('list()', () => {
310
310
  assert.ok(humanIds.includes('F-1'), 'F-1 stayed human');
311
311
  assert.ok(!humanIds.includes('F-4'), 'F-4 is now an agent claim');
312
312
 
313
- const byOdin = br.list({ claimantId: 'odin' });
314
- assert.equal(byOdin.length, 1, 'only F-1 still belongs to odin');
313
+ const byOdin = br.list({ claimantId: 'mike' });
314
+ assert.equal(byOdin.length, 1, 'only F-1 still belongs to mike');
315
315
  assert.equal(byOdin[0].id, 'F-1');
316
316
  });
317
317
 
@@ -327,9 +327,9 @@ describe('persistence', () => {
327
327
  test('bridges are independent — each gets its own file', () => {
328
328
  const file = writeFixture(tmp, `p-${Math.random().toString(36).slice(2, 6)}.json`, [minimalFeature('F-P')]);
329
329
  const a = bridgeFactory(file);
330
- a.claim('F-P', { type: 'human', id: 'odin' }, '');
330
+ a.claim('F-P', { type: 'human', id: 'mike' }, '');
331
331
  const b = bridgeFactory(file);
332
- assert.equal(b.get('F-P').claimant.id, 'odin');
332
+ assert.equal(b.get('F-P').claimant.id, 'mike');
333
333
  assert.equal(b.get('F-P').claimStatus, 'claimed');
334
334
  });
335
335
 
@@ -1,33 +1,33 @@
1
1
  #!/usr/bin/env bash
2
2
  #
3
- # cli/agent-browser-up.sh — install/launch agent-browser for Cline.
3
+ # cli/kevin-up.sh — install/launch kevin for Cline.
4
4
  # v6.0.0 — Replaces browser-harness (Python, v3.20.7-v5.6.0) with
5
- # agent-browser (native Rust CLI from vercel-labs, ~38K★).
5
+ # kevin (native Rust CLI from vercel-labs, ~38K★).
6
6
  #
7
- # agent-browser is a thin CLI wrapper around Chrome for Testing that
7
+ # kevin is a thin CLI wrapper around Chrome for Testing that
8
8
  # gives agents access to:
9
9
  # - 100+ typed CLI commands (open, snapshot, click, fill, screenshot, …)
10
- # - Native MCP stdio server (`agent-browser mcp`)
10
+ # - Native MCP stdio server (`kevin mcp`)
11
11
  # - Self-healing snapshot-based element detection
12
12
  # - Plugins (vault, recorder, …)
13
13
  # - Vercel AI SDK + AI Gateway integration (natural-language `chat`)
14
14
  #
15
- # Install: npm install -g agent-browser
16
- # agent-browser install # downloads Chrome for Testing
15
+ # Install: npm install -g kevin
16
+ # kevin install # downloads Chrome for Testing
17
17
  #
18
18
  # This script ensures the daemon is up before Bizar agents try to
19
19
  # drive the browser. Idempotent: re-running is a no-op if already up.
20
20
  #
21
21
  # Usage:
22
- # cli/agent-browser-up.sh # start if not running
23
- # cli/agent-browser-up.sh status # print status
24
- # cli/agent-browser-up.sh stop # kill daemon
25
- # cli/agent-browser-up.sh restart # stop + start
26
- # cli/agent-browser-up.sh install # install + bootstrap
27
- # cli/agent-browser-up.sh doctor # run agent-browser doctor
22
+ # cli/kevin-up.sh # start if not running
23
+ # cli/kevin-up.sh status # print status
24
+ # cli/kevin-up.sh stop # kill daemon
25
+ # cli/kevin-up.sh restart # stop + start
26
+ # cli/kevin-up.sh install # install + bootstrap
27
+ # cli/kevin-up.sh doctor # run kevin doctor
28
28
  #
29
29
  # Environment overrides:
30
- # AB_PROFILE — user-data-dir (default: ~/.agent-browser/profile)
30
+ # AB_PROFILE — user-data-dir (default: ~/.kevin/profile)
31
31
  # AGENT_BROWSER_API_KEY — for natural-language chat
32
32
  #
33
33
  # Exit codes:
@@ -36,23 +36,23 @@
36
36
  #
37
37
  set -euo pipefail
38
38
 
39
- AB_BIN="${AB_BIN:-$(command -v agent-browser || true)}"
39
+ AB_BIN="${AB_BIN:-$(command -v kevin || true)}"
40
40
  AB_DAEMON_HOST="127.0.0.1"
41
41
  AB_DAEMON_PORT="${AB_DAEMON_PORT:-9223}"
42
- AB_PROFILE="${AB_PROFILE:-$HOME/.agent-browser/profile}"
42
+ AB_PROFILE="${AB_PROFILE:-$HOME/.kevin/profile}"
43
43
 
44
- log() { echo "[agent-browser-up] $*" >&2; }
44
+ log() { echo "[kevin-up] $*" >&2; }
45
45
  fail() { log "FAIL: $*"; exit 1; }
46
46
 
47
47
  ensure_binary() {
48
48
  if [ -z "$AB_BIN" ] || ! [ -x "$AB_BIN" ]; then
49
- log "agent-browser not on PATH. Installing via npm..."
49
+ log "kevin not on PATH. Installing via npm..."
50
50
  command -v npm >/dev/null || fail "npm not found — install Node.js 24+ first"
51
- npm install -g agent-browser
52
- AB_BIN="$(command -v agent-browser)"
53
- [ -x "$AB_BIN" ] || fail "agent-browser install failed"
51
+ npm install -g kevin
52
+ AB_BIN="$(command -v kevin)"
53
+ [ -x "$AB_BIN" ] || fail "kevin install failed"
54
54
  fi
55
- log "agent-browser binary: $AB_BIN"
55
+ log "kevin binary: $AB_BIN"
56
56
  log "version: $($AB_BIN --version 2>/dev/null || echo 'unknown')"
57
57
  }
58
58
 
@@ -61,7 +61,7 @@ ensure_chrome() {
61
61
  log "Chrome profile already exists: $AB_PROFILE"
62
62
  return 0
63
63
  fi
64
- log "Bootstrapping Chrome for Testing via agent-browser install"
64
+ log "Bootstrapping Chrome for Testing via kevin install"
65
65
  $AB_BIN install --silent || true
66
66
  }
67
67
 
@@ -72,7 +72,7 @@ is_daemon_running() {
72
72
  }
73
73
 
74
74
  start_daemon() {
75
- log "Starting agent-browser daemon (profile: $AB_PROFILE, port: $AB_DAEMON_PORT)..."
75
+ log "Starting kevin daemon (profile: $AB_PROFILE, port: $AB_DAEMON_PORT)..."
76
76
  mkdir -p "$AB_PROFILE"
77
77
 
78
78
  # Spawn the daemon detached so it survives parent shell exit
@@ -89,13 +89,13 @@ start_daemon() {
89
89
  local retries=30
90
90
  while [ $retries -gt 0 ]; do
91
91
  if curl -fsS --max-time 2 "http://${AB_DAEMON_HOST}:${AB_DAEMON_PORT}/json/version" >/dev/null 2>&1; then
92
- log "agent-browser daemon is up (pid $pid)"
92
+ log "kevin daemon is up (pid $pid)"
93
93
  return 0
94
94
  fi
95
95
  sleep 1
96
96
  retries=$((retries - 1))
97
97
  done
98
- fail "agent-browser daemon did not bind to ${AB_DAEMON_HOST}:${AB_DAEMON_PORT}"
98
+ fail "kevin daemon did not bind to ${AB_DAEMON_HOST}:${AB_DAEMON_PORT}"
99
99
  }
100
100
 
101
101
  stop_daemon() {
@@ -104,24 +104,24 @@ stop_daemon() {
104
104
  local pid
105
105
  pid="$(cat "$pidfile" 2>/dev/null || true)"
106
106
  if [ -n "$pid" ] && kill -0 "$pid" 2>/dev/null; then
107
- log "Stopping agent-browser daemon (pid $pid)"
107
+ log "Stopping kevin daemon (pid $pid)"
108
108
  kill "$pid" 2>/dev/null || true
109
109
  sleep 1
110
110
  kill -9 "$pid" 2>/dev/null || true
111
111
  fi
112
112
  rm -f "$pidfile"
113
113
  fi
114
- # Belt-and-braces: kill any orphan agent-browser processes
115
- pkill -f 'agent-browser serve' 2>/dev/null || true
114
+ # Belt-and-braces: kill any orphan kevin processes
115
+ pkill -f 'kevin serve' 2>/dev/null || true
116
116
  }
117
117
 
118
118
  print_status() {
119
119
  if is_daemon_running; then
120
- echo "agent-browser: RUNNING (http://${AB_DAEMON_HOST}:${AB_DAEMON_PORT})"
120
+ echo "kevin: RUNNING (http://${AB_DAEMON_HOST}:${AB_DAEMON_PORT})"
121
121
  $AB_BIN doctor 2>&1 | tail -20 || true
122
122
  else
123
- echo "agent-browser: STOPPED"
124
- echo "Start with: cli/agent-browser-up.sh start"
123
+ echo "kevin: STOPPED"
124
+ echo "Start with: cli/kevin-up.sh start"
125
125
  fi
126
126
  }
127
127
 
@@ -134,17 +134,17 @@ case "${1:-start}" in
134
134
  install) ensure_binary; ensure_chrome; $AB_BIN install ;;
135
135
  help|--help|-h)
136
136
  cat <<'AGENT_BROWSER_UP_HELP'
137
- agent-browser-up -- start/stop the agent-browser daemon
137
+ kevin-up -- start/stop the kevin daemon
138
138
 
139
- Usage: cli/agent-browser-up.sh <command>
139
+ Usage: cli/kevin-up.sh <command>
140
140
 
141
141
  Commands:
142
142
  start Start the daemon (idempotent)
143
143
  stop Stop the daemon
144
144
  restart Stop + start
145
145
  status Print daemon status
146
- doctor Run agent-browser doctor
147
- install Install agent-browser + download Chrome
146
+ doctor Run kevin doctor
147
+ install Install kevin + download Chrome
148
148
  AGENT_BROWSER_UP_HELP
149
149
  exit 0
150
150
  ;;