@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.
- package/bizar-dash/tests/memory-protocol-drift.test.mjs +18 -11
- package/cli/__tests__/feature-list-bridge.test.mjs +42 -42
- package/cli/agent-browser-up.sh +35 -35
- package/cli/agent-browser-update.mjs +42 -42
- package/cli/agent-browser-update.test.mjs +7 -7
- package/cli/audit.mjs +3 -3
- package/cli/bin.mjs +10 -10
- package/cli/bootstrap.mjs +1 -1
- package/cli/cli-commands-validation.test.mjs +2 -2
- package/cli/commands/claim.mjs +3 -3
- package/cli/commands/claude-cmd.mjs +3 -3
- package/cli/commands/eval.mjs +6 -6
- package/cli/commands/util.mjs +18 -18
- package/cli/commands/validate.mjs +10 -10
- package/cli/init.mjs +1 -1
- package/cli/install/postinstall.mjs +4 -4
- package/cli/plow-through.test.mjs +7 -6
- package/cli/prompts.mjs +11 -11
- package/cli/provision.mjs +4 -16
- package/config/skills/bizar/SKILL.md +1 -1
- package/config/skills/skillopt/SKILL.md +129 -0
- package/config/skills/thinking-archetypes/SKILL.md +90 -0
- package/config/skills/thinking-bayesian/SKILL.md +267 -0
- package/config/skills/thinking-bounded-rationality/SKILL.md +406 -0
- package/config/skills/thinking-circle-of-competence/SKILL.md +216 -0
- package/config/skills/thinking-cynefin/SKILL.md +70 -0
- package/config/skills/thinking-debiasing/SKILL.md +192 -0
- package/config/skills/thinking-dual-process/SKILL.md +282 -0
- package/config/skills/thinking-effectuation/SKILL.md +366 -0
- package/config/skills/thinking-feedback-loops/SKILL.md +464 -0
- package/config/skills/thinking-fermi-estimation/SKILL.md +263 -0
- package/config/skills/thinking-first-principles/SKILL.md +167 -0
- package/config/skills/thinking-five-whys-plus/SKILL.md +139 -0
- package/config/skills/thinking-inversion/SKILL.md +195 -0
- package/config/skills/thinking-jobs-to-be-done/SKILL.md +363 -0
- package/config/skills/thinking-kepner-tregoe/SKILL.md +154 -0
- package/config/skills/thinking-leverage-points/SKILL.md +390 -0
- package/config/skills/thinking-lindy-effect/SKILL.md +331 -0
- package/config/skills/thinking-map-territory/SKILL.md +111 -0
- package/config/skills/thinking-margin-of-safety/SKILL.md +330 -0
- package/config/skills/thinking-model-combination/SKILL.md +406 -0
- package/config/skills/thinking-model-router/SKILL.md +360 -0
- package/config/skills/thinking-model-selection/SKILL.md +341 -0
- package/config/skills/thinking-occams-razor/SKILL.md +129 -0
- package/config/skills/thinking-ooda/SKILL.md +127 -0
- package/config/skills/thinking-opportunity-cost/SKILL.md +360 -0
- package/config/skills/thinking-pre-mortem/SKILL.md +170 -0
- package/config/skills/thinking-probabilistic/SKILL.md +324 -0
- package/config/skills/thinking-red-team/SKILL.md +142 -0
- package/config/skills/thinking-regret-minimization/SKILL.md +335 -0
- package/config/skills/thinking-reversibility/SKILL.md +326 -0
- package/config/skills/thinking-scientific-method/SKILL.md +162 -0
- package/config/skills/thinking-second-order/SKILL.md +184 -0
- package/config/skills/thinking-socratic/SKILL.md +198 -0
- package/config/skills/thinking-steel-manning/SKILL.md +332 -0
- package/config/skills/thinking-systems/SKILL.md +238 -0
- package/config/skills/thinking-theory-of-constraints/SKILL.md +338 -0
- package/config/skills/thinking-thought-experiment/SKILL.md +354 -0
- package/config/skills/thinking-triz/SKILL.md +171 -0
- package/config/skills/thinking-via-negativa/SKILL.md +358 -0
- package/package.json +2 -3
- package/packages/sdk/dist/consensus/index.js +4 -4
- package/packages/sdk/dist/router/agent-model-registry.js +2 -2
- package/packages/sdk/dist/router/index.d.ts +3 -3
- package/packages/sdk/dist/router/index.js +4 -4
- package/packages/sdk/dist/router/q-learning-router.d.ts +2 -2
- package/packages/sdk/dist/router/q-learning-router.js +18 -18
- package/scripts/bh-full-e2e.mjs +10 -8
- package/scripts/check-agents.mjs +27 -24
- package/scripts/mirror-claude-md.sh +0 -0
- package/scripts/sync-skills-mirror.mjs +113 -0
- package/scripts/verify-thinking-skills.mjs +135 -0
- package/config/agents/_shared/AGENT_BASELINE.md +0 -155
- package/config/agents/_shared/CLINE_TOOLS.md +0 -398
- package/config/agents/_shared/SKILLS.md +0 -109
- package/config/agents/agent-browser.md +0 -90
- package/config/agents/baldr.md +0 -60
- package/config/agents/forseti.md +0 -60
- package/config/agents/frigg.md +0 -52
- package/config/agents/heimdall.md +0 -44
- package/config/agents/hermod.md +0 -64
- package/config/agents/mimir.md +0 -60
- package/config/agents/odin.md +0 -310
- package/config/agents/quick.md +0 -47
- package/config/agents/semble-search.md +0 -59
- package/config/agents/thor.md +0 -64
- package/config/agents/tyr.md +0 -67
- package/config/agents/vidarr.md +0 -65
- package/config/agents/vor.md +0 -62
- package/config/commands/audit.md +0 -5
- package/config/commands/bizar.md +0 -22
- package/config/commands/explain.md +0 -5
- package/config/commands/init.md +0 -26
- package/config/commands/learn.md +0 -5
- package/config/commands/plan.md +0 -30
- package/config/commands/plow-through.md +0 -47
- package/config/commands/pr-review.md +0 -5
- package/config/commands/setup-provider.md +0 -95
- package/config/commands/tailscale-serve.md +0 -97
- package/config/commands/team.md +0 -124
- package/config/commands/test.md +0 -60
- package/config/commands/validate.md +0 -66
- package/config/commands/visual-plan.md +0 -19
- package/config/hooks/PostToolUse +0 -66
- package/config/hooks/PreToolUse +0 -82
- package/config/hooks/README.md +0 -68
- package/config/hooks/TaskResume +0 -48
- package/config/hooks/TaskStart +0 -47
- 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('
|
|
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
|
-
|
|
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
|
|
31
|
-
const baseline = read('
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
const
|
|
37
|
-
|
|
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('
|
|
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: '
|
|
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, '
|
|
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: '
|
|
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: '
|
|
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: '
|
|
120
|
-
br.transition('F-A', 'completed', { id: '
|
|
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: '
|
|
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: '
|
|
139
|
-
const result = br.release('F-X', { id: '
|
|
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: '
|
|
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: '
|
|
154
|
+
br.claim('F-X', { type: 'human', id: 'mike' }, '');
|
|
155
155
|
assert.throws(
|
|
156
|
-
() => br.release('F-X', { id: '
|
|
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: '
|
|
173
|
+
br.claim('F-H', { type: 'human', id: 'mike' }, '');
|
|
174
174
|
const result = br.handoff(
|
|
175
175
|
'F-H',
|
|
176
|
-
{ type: 'human', id: '
|
|
177
|
-
{ type: 'agent', id: '
|
|
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, '
|
|
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: '
|
|
193
|
-
{ type: 'agent', id: '
|
|
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: '
|
|
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: '
|
|
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, '
|
|
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: '
|
|
231
|
+
br.claim('F-S', { type: 'human', id: 'mike' }, '');
|
|
232
232
|
assert.throws(
|
|
233
|
-
() => br.steal('F-S', { type: 'agent', id: '
|
|
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: '
|
|
251
|
-
br.transition('F-T', 'active', { id: '
|
|
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: '
|
|
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: '
|
|
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: '
|
|
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: '
|
|
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: '
|
|
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: '
|
|
271
|
+
br.claim('F-T', { type: 'human', id: 'mike' }, '');
|
|
272
272
|
assert.throws(
|
|
273
|
-
() => br.transition('F-T', 'bogus', { id: '
|
|
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: '
|
|
296
|
-
br.claim('F-2', { type: 'agent', id: '
|
|
297
|
-
br.claim('F-3', { type: 'human', id: '
|
|
298
|
-
br.handoff('F-3', { type: 'human', id: '
|
|
299
|
-
br.claim('F-4', { type: 'human', id: '
|
|
300
|
-
br.steal('F-4', { type: 'agent', id: '
|
|
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: '
|
|
314
|
-
assert.equal(byOdin.length, 1, 'only F-1 still belongs to
|
|
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: '
|
|
330
|
+
a.claim('F-P', { type: 'human', id: 'mike' }, '');
|
|
331
331
|
const b = bridgeFactory(file);
|
|
332
|
-
assert.equal(b.get('F-P').claimant.id, '
|
|
332
|
+
assert.equal(b.get('F-P').claimant.id, 'mike');
|
|
333
333
|
assert.equal(b.get('F-P').claimStatus, 'claimed');
|
|
334
334
|
});
|
|
335
335
|
|
package/cli/agent-browser-up.sh
CHANGED
|
@@ -1,33 +1,33 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
2
|
#
|
|
3
|
-
# cli/
|
|
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
|
-
#
|
|
5
|
+
# kevin (native Rust CLI from vercel-labs, ~38K★).
|
|
6
6
|
#
|
|
7
|
-
#
|
|
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 (`
|
|
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
|
|
16
|
-
#
|
|
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/
|
|
23
|
-
# cli/
|
|
24
|
-
# cli/
|
|
25
|
-
# cli/
|
|
26
|
-
# cli/
|
|
27
|
-
# cli/
|
|
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: ~/.
|
|
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
|
|
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/.
|
|
42
|
+
AB_PROFILE="${AB_PROFILE:-$HOME/.kevin/profile}"
|
|
43
43
|
|
|
44
|
-
log() { echo "[
|
|
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 "
|
|
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
|
|
52
|
-
AB_BIN="$(command -v
|
|
53
|
-
[ -x "$AB_BIN" ] || fail "
|
|
51
|
+
npm install -g kevin
|
|
52
|
+
AB_BIN="$(command -v kevin)"
|
|
53
|
+
[ -x "$AB_BIN" ] || fail "kevin install failed"
|
|
54
54
|
fi
|
|
55
|
-
log "
|
|
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
|
|
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
|
|
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 "
|
|
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 "
|
|
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
|
|
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
|
|
115
|
-
pkill -f '
|
|
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 "
|
|
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 "
|
|
124
|
-
echo "Start with: cli/
|
|
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
|
-
|
|
137
|
+
kevin-up -- start/stop the kevin daemon
|
|
138
138
|
|
|
139
|
-
Usage: cli/
|
|
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
|
|
147
|
-
install Install
|
|
146
|
+
doctor Run kevin doctor
|
|
147
|
+
install Install kevin + download Chrome
|
|
148
148
|
AGENT_BROWSER_UP_HELP
|
|
149
149
|
exit 0
|
|
150
150
|
;;
|