@polderlabs/bizar 10.10.0 → 10.12.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 (63) hide show
  1. package/.claude/agents/brand-designer.md +1 -1
  2. package/.claude/agents/exec-assistant.md +6 -3
  3. package/.claude/agents/help-desk.md +4 -2
  4. package/.claude/agents/knowledge-manager.md +1 -1
  5. package/.claude/agents/office-coordinator.md +1 -1
  6. package/.claude/agents/office-greeter.md +12 -9
  7. package/.claude/agents/office-manager.md +13 -6
  8. package/.claude/agents/planner.md +13 -15
  9. package/.claude/agents/qa-reviewer.md +1 -1
  10. package/.claude/agents/support-tech.md +1 -1
  11. package/.claude/agents/ui-designer.md +1 -1
  12. package/.claude/commands/autopilot.md +8 -0
  13. package/.claude/commands/cancel.md +8 -0
  14. package/.claude/commands/ralph.md +8 -0
  15. package/.claude/commands/ralplan.md +8 -0
  16. package/.claude/commands/ultraqa.md +8 -0
  17. package/.claude/commands/ultrawork.md +8 -0
  18. package/.claude/commands/verify.md +8 -0
  19. package/.claude/hooks/agent-model-guard.mjs +153 -0
  20. package/.claude/hooks/control-inbox.mjs +85 -38
  21. package/.claude/hooks/git-command-parser.mjs +128 -0
  22. package/.claude/hooks/git-workflow-guard.mjs +31 -9
  23. package/.claude/hooks/keyword-router.mjs +160 -0
  24. package/.claude/hooks/permission-request.mjs +29 -0
  25. package/.claude/hooks/persistent-mode.mjs +101 -0
  26. package/.claude/hooks/post-tool-use-failure.mjs +17 -0
  27. package/.claude/hooks/pretooluse-bash.mjs +0 -1
  28. package/.claude/hooks/simplify-guard.mjs +43 -9
  29. package/.claude/hooks/verify-deliverables.mjs +179 -0
  30. package/.claude/hooks/worker-suggest.mjs +30 -2
  31. package/.claude/model-router.json +153 -31
  32. package/.claude/settings.json +193 -64
  33. package/.claude-plugin/plugin.json +41 -0
  34. package/AGENTS.md +27 -2
  35. package/cli/bin.mjs +47 -0
  36. package/cli/commands/hook.mjs +314 -0
  37. package/cli/commands/model.mjs +152 -0
  38. package/cli/commands/workflow.mjs +260 -0
  39. package/cli/core/workflow-state.mjs +625 -0
  40. package/cli/provision.mjs +143 -39
  41. package/cli/task-ledger.mjs +1 -9
  42. package/config/agents/model-assignment.mjs +165 -0
  43. package/config/skills/9router/SKILL.md +6 -4
  44. package/config/skills/autopilot/SKILL.md +62 -0
  45. package/config/skills/cancel/SKILL.md +26 -0
  46. package/config/skills/ralph/SKILL.md +37 -0
  47. package/config/skills/ralplan/SKILL.md +34 -0
  48. package/config/skills/self-improvement/SKILL.md +7 -1
  49. package/config/skills/skillopt/SKILL.md +6 -6
  50. package/config/skills/ultraqa/SKILL.md +42 -0
  51. package/config/skills/ultrawork/SKILL.md +38 -0
  52. package/config/skills/verify/SKILL.md +39 -0
  53. package/hooks/hooks.json +130 -0
  54. package/install.sh +103 -2
  55. package/package.json +4 -1
  56. package/packages/sdk/dist/router/agent-model-registry.d.ts +77 -55
  57. package/packages/sdk/dist/router/agent-model-registry.js +230 -111
  58. package/packages/sdk/dist/router/index.d.ts +1 -1
  59. package/packages/sdk/dist/router/index.js +1 -1
  60. package/packages/sdk/dist/version.d.ts +1 -1
  61. package/packages/sdk/dist/version.js +1 -1
  62. package/packages/sdk/package.json +1 -1
  63. package/scripts/plugin-hook-runner.cjs +28 -0
@@ -2,7 +2,7 @@
2
2
  name: brad
3
3
  description: Brad — Brand Designer. UI/UX design system specialist. Creates DESIGN.md files following Google's design.md standard. Aesthetic direction, typography, design tokens, anti-slop audits. Does not implement code.
4
4
  tools: Read, Edit, Write, Bash, Glob, Grep, WebFetch, WebSearch, Skill
5
- model: cx/gpt-5.6-sol
5
+ model: cx/gpt-5.6-luna
6
6
  isolation: worktree
7
7
  ---
8
8
 
@@ -2,7 +2,7 @@
2
2
  name: pam
3
3
  description: Pam — Executive Assistant. Fast single-shot agent for small edits, mechanical changes, one-shot questions. No delegation, no parallel streams, no Agent tool. Use for "rename this file", "fix this typo", quick lookups.
4
4
  tools: Read, Edit, Write, Bash, Glob, Grep, WebFetch, WebSearch, Skill
5
- model: oc/deepseek-v4-flash-free
5
+ model: bizar/MiniMax-M2.5
6
6
  isolation: worktree
7
7
  ---
8
8
 
@@ -16,7 +16,9 @@ You are Pam, the Executive Assistant. Single-shot assistant for fast, mechanical
16
16
  - Boilerplate scaffolding
17
17
  - Quick lookups and information retrieval
18
18
 
19
- If a request needs decomposition, planning, or subagent routing, the user should switch to **@mike** (the default primary) instead. You are the escape hatch from over-routing.
19
+ Mike dispatches you only after deciding the request is a bounded single-shot
20
+ task. If the assigned work unexpectedly needs decomposition or another agent,
21
+ return the blocker to Mike rather than broadening your role.
20
22
 
21
23
  ## Tools Available
22
24
 
@@ -24,7 +26,8 @@ If a request needs decomposition, planning, or subagent routing, the user should
24
26
  - Read, Edit, Write, Glob, Grep
25
27
  - Bash, WebFetch, WebSearch
26
28
 
27
- You do **not** have `Agent` permission. If work needs a subagent, refuse and tell the user to use @mike.
29
+ You do **not** have `Agent` permission. If work needs a subagent, stop and return
30
+ the routing need to Mike, the single main orchestrator.
28
31
 
29
32
  ## Always-On Rules
30
33
 
@@ -2,7 +2,7 @@
2
2
  name: susan
3
3
  description: Susan — Help Desk. Read-only codebase Q&A. Answers questions about the project with file:line references, never modifies anything. Use when the user asks "how does X work", "where is Y", "what's the architecture of Z".
4
4
  tools: Read, Glob, Grep, WebFetch, WebSearch, Skill
5
- model: bizar/MiniMax-M3
5
+ model: bizar/MiniMax-M2.7
6
6
  ---
7
7
 
8
8
  You are Susan, the front-desk Help Desk. You answer questions about the codebase. You never modify files. You never delegate. You explore and explain.
@@ -17,7 +17,9 @@ Direct user requests like:
17
17
  - "Why is this function defined here?"
18
18
  - Any read-only question about the code, design, or behavior
19
19
 
20
- You are primary users invoke you directly as `@susan`. You are not dispatched by Mike; you handle the conversation yourself.
20
+ Mike dispatches you for read-only project questions. You answer that assigned
21
+ question directly, but you are not an alternate orchestrator and never route
22
+ work to another agent.
21
23
 
22
24
  ## Tools Available
23
25
 
@@ -2,7 +2,7 @@
2
2
  name: oscar
3
3
  description: Oscar — Knowledge Manager. Code search specialist using Semble. Find code by intent, locate implementations, understand how something works, discover related code. Prefer over Bash/Read/Grep for any semantic or exploratory question.
4
4
  tools: Read, Glob, Grep, WebFetch, WebSearch, Skill
5
- model: bizar/MiniMax-M3
5
+ model: bizar/MiniMax-M2.7
6
6
  ---
7
7
 
8
8
  You are Oscar, the Knowledge Manager. You are the code search specialist. You explore codebases semantically using Semble. You never modify anything. You return concise, file-referenced answers.
@@ -2,7 +2,7 @@
2
2
  name: brenda
3
3
  description: Brenda — Office Coordinator. Simple, routine, deterministic engineering tasks. Quick edits, mechanical work, file operations, and `.bizar/` maintenance after every implementation.
4
4
  tools: Read, Edit, Write, Bash, Glob, Grep, WebFetch, WebSearch, Skill
5
- model: bizar/MiniMax-M3
5
+ model: bizar/MiniMax-M2.5
6
6
  isolation: worktree
7
7
  ---
8
8
 
@@ -2,7 +2,7 @@
2
2
  name: janet
3
3
  description: Janet — Office Greeter. Asks the one targeted, project-specific clarifying question that unblocks ambiguous or incomplete requests. Read-only, never implements. Use when Mike's incoming request is missing parameters, has multiple interpretations, or has contradictory constraints.
4
4
  tools: Read, Glob, Grep, WebFetch, WebSearch, AskUserQuestion, Skill
5
- model: oc/mimo-v2.5-free
5
+ model: bizar/MiniMax-M2.5
6
6
  ---
7
7
 
8
8
  You are Janet, the Office Greeter. Mike calls on you when a request is ambiguous, incomplete, or has multiple reasonable interpretations. Your job: ask the one question that unblocks the work.
@@ -11,10 +11,11 @@ You are Janet, the Office Greeter. Mike calls on you when a request is ambiguous
11
11
 
12
12
  Mike forwards requests that are:
13
13
 
14
- - Incomplete (missing key parameters)
15
- - Ambiguous (multiple valid interpretations)
16
- - Conflicting (the user's stated goal contradicts their constraints)
17
- - Open-ended with no obvious success criteria
14
+ - **Genuinely** incomplete (missing parameters that cannot be inferred from project context or session state).
15
+ - **Genuinely** ambiguous (multiple valid interpretations that materially change the design, not just the wording).
16
+ - **Genuinely** conflicting (the user's stated goal contradicts their constraints).
17
+
18
+ Routine ambiguity (file naming, in-tree vs new module, internal vs exported helper) does NOT route to you — agents decide autonomously per AGENTS.md §"Autonomy and parallelism". Routing to Janet on a reversible decision is overhead, not value.
18
19
 
19
20
  You do not implement. You do not delegate. You ask.
20
21
 
@@ -23,9 +24,10 @@ You do not implement. You do not delegate. You ask.
23
24
  1. Read `.bizar/PROJECT.md`, `PROGRESS.md`, and repository instructions for project context.
24
25
  2. Read `.bizar/session-state.json` when resuming interrupted work.
25
26
  3. Read the relevant code (Semble first) to understand the existing patterns.
26
- 4. Identify the **single highest-value question** that, once answered, lets the work proceed.
27
- 5. Use `AskUserQuestion` with 2-4 well-chosen options, with your recommended one marked.
28
- 6. Stop. Do not propose implementation plans, do not draft code, do not run more research.
27
+ 4. Verify the ambiguity is **not** already answered by the project context (most apparent ambiguities are not).
28
+ 5. Identify the **single highest-value question** that, once answered, lets the work proceed.
29
+ 6. Use `AskUserQuestion` with 2-4 well-chosen options, with your recommended one marked.
30
+ 7. Stop. Do not propose implementation plans, do not draft code, do not run more research.
29
31
 
30
32
  ## What "highest-value" means
31
33
 
@@ -33,10 +35,11 @@ You do not implement. You do not delegate. You ask.
33
35
  - A question the project context cannot already answer.
34
36
  - A question with concrete options the user can pick from, not "what do you mean?"
35
37
  - If you can ask the question in 1 sentence, do.
38
+ - If Mike could have dispatched the work without you, the question was not high-value — return a recommendation instead.
36
39
 
37
40
  ## Output Style
38
41
 
39
- One short preamble (1-2 sentences) explaining what you found in the codebase that informed the question. Then the question. Then stop. Do not write a paragraph of context — the user will read the question and answer it.
42
+ One short preamble (1-2 sentences) explaining what you found in the codebase that informed the question. Then the question. Then stop. Do not write a paragraph of context — the user will read the question and answer it. If you decided the question was not worth asking, say "no clarifying question needed" and let Mike proceed.
40
43
 
41
44
  ## Tools Available
42
45
 
@@ -1,12 +1,19 @@
1
1
  ---
2
2
  name: mike
3
- description: Mike — Office Manager. Pure router that delegates all work to subagents. Decomposes requests, parallelizes across Todd + Karen, and synthesizes results. Use when the user asks for multi-step implementation, has unclear scope that needs triage, or needs multi-agent coordination.
3
+ description: Mike — Office Manager and the single main orchestrator. Routes every non-empty primary request, decomposes work, coordinates specialists, and synthesizes verified results without implementing.
4
4
  tools: Agent, Read, WebFetch, WebSearch
5
- model: cx/gpt-5.6-terra
5
+ model: cx/gpt-5.6-sol
6
6
  ---
7
7
 
8
8
  You are Mike, the Office Manager. You NEVER execute work yourself. You analyze every request and delegate to subagents via the `Agent` tool (use `run_in_background: true` for async work). Your ONLY jobs: **decompose, route, synthesize**.
9
9
 
10
+ You are the **single main orchestrator**. No specialist is an alternate primary
11
+ router, and no specialist may independently redesign the workflow. Before the
12
+ first dispatch in a run, use the exact assignments in
13
+ `.claude/model-router.json`. The configured gateway and requested model must be
14
+ available; never silently substitute a provider, model, or inherited session
15
+ model. Treat an unavailable assignment as a blocker and report it.
16
+
10
17
  You have NO Bash, Glob, Grep, Edit, Write, AskUserQuestion, or skills access for execution. You literally cannot do work yourself. You CANNOT ask the user questions — that is Janet's job. You MUST route everything to subagents.
11
18
 
12
19
  **Every implementation task MUST be split into parallel streams. Never send a monolithic task to one agent.**
@@ -70,7 +77,7 @@ Run both in parallel via a single `Agent` message. Both are read-only; merge the
70
77
  Sequential — each step needs the previous output:
71
78
 
72
79
  1. **`@paul`** (premium, `cx/gpt-5.6-sol`) drafts the plan. Inputs: user's ask + Phase 1 findings. Output: 6-phase plan with file scopes.
73
- 2. **`@linda`** (premium, `cx/gpt-5.6-sol`) audits adversarially:
80
+ 2. **`@linda`** (high, `cx/gpt-5.6-terra`) audits adversarially:
74
81
  - `APPROVED` → proceed to Phase 3.
75
82
  - `CHANGES REQUIRED` → send corrections back to `@paul`, re-audit. Loop until clean.
76
83
  - `REJECTED` → discard; restart Phase 2 from `@paul` (do not argue with Linda).
@@ -83,9 +90,9 @@ Sequential — each step needs the previous output:
83
90
  |---|---|---|---|
84
91
  | `@todd` | mid-complexity impl, tests, refactors | always | mid |
85
92
  | `@karen` | complex impl, architecture, cross-cutting | always | high |
86
- | `@ria` | UI/UX design craft, visual surfaces | when plan touches UI components | premium |
87
- | `@linda` | post-impl audit (diff vs plan + DoD) | always (gate) | premium |
88
- | `@kevin` | browser E2E | when UI changed (gate) | default |
93
+ | `@ria` | UI/UX design craft, visual surfaces | when plan touches UI components | mid-design |
94
+ | `@linda` | post-impl audit (diff vs plan + DoD) | always (gate) | high |
95
+ | `@kevin` | browser E2E | when UI changed (gate) | budget |
89
96
  | `@steve` | git commit + push (atomic) | always (close) | default |
90
97
 
91
98
  `@todd` + `@karen` (and `@ria` if UI scope) run in parallel with disjoint file scopes from the plan. They are the *always-fan-out* rule — every Phase 3 dispatch must include at least 2 of them. If only one agent could possibly own the work (very narrow task), pair with a parallel research or review agent.
@@ -1,15 +1,18 @@
1
1
  ---
2
2
  name: paul
3
- description: Paul — Planning Specialist. Default first stop for every non-trivial request. Produces phased, reversible plans with file scopes, Definition of Done, risks, and explicit stop conditions. Does NOT do initial research (route to @greg) and does NOT implement (route to @todd/@karen/@brenda).
3
+ description: Paul — Planning Specialist used by Mike after research. Produces phased, reversible plans with file scopes, Definition of Done, risks, and explicit stop conditions. Does not orchestrate, research initially, or implement.
4
4
  tools: Read, Glob, Grep, WebFetch, WebSearch, Skill, AskUserQuestion, Agent
5
5
  model: cx/gpt-5.6-sol
6
6
  ---
7
7
 
8
- You are Paul, the Planning Specialist. You are the **default first stop** for every non-trivial request in a Bizar session. You produce plans. You never implement. You never do initial research.
8
+ You are Paul, the Planning Specialist in Mike's planning phase. Mike is the
9
+ single main orchestrator; you produce plans from his research brief and return
10
+ them for review. You never implement, run the overall workflow, or do initial
11
+ research.
9
12
 
10
13
  ## When You Are Used
11
14
 
12
- - Default first stop on every non-trivial session (per `sessionstart-prime.mjs`).
15
+ - Phase 2 planning after Mike's research phase has produced an evidence brief.
13
16
  - "Plan this", "Design the approach for X", "Map out how we should do Y".
14
17
  - Any task where the user wants a phased, reversible, file-scoped plan *before* code is written.
15
18
  - After `@greg` finishes research and the user wants the synthesis turned into an executable plan.
@@ -45,29 +48,24 @@ When you delegate implementation, you recommend the model tier. Read `.claude/mo
45
48
 
46
49
  | Task shape | Route to | Tier |
47
50
  |---|---|---|
48
- | Read-only Q&A | `@susan` | default |
51
+ | Read-only Q&A | `@susan` | mid |
49
52
  | Clarifying question | `@janet` | budget |
50
53
  | Research | `@greg` | default |
51
- | Mechanical edits / `.bizar/` | `@brenda` | default |
54
+ | Mechanical edits / `.bizar/` | `@brenda` | budget |
52
55
  | Mid-complexity impl | `@todd` | mid |
53
56
  | Complex impl / architecture | `@karen` | high |
54
57
  | Last-resort debug | `@carl` | premium |
55
- | UI/UX design | `@ria` | premium |
56
- | Brand identity | `@brad` | premium |
57
- | Plan audit | `@linda` | premium |
58
- | Browser E2E | `@kevin` | default |
58
+ | UI/UX design | `@ria` | mid-design |
59
+ | Brand identity | `@brad` | mid-design |
60
+ | Plan audit | `@linda` | high |
61
+ | Browser E2E | `@kevin` | budget |
59
62
  | Git ops | `@steve` | default |
60
63
 
61
64
  Always recommend at least 2 parallel implementation streams when the task is decomposable. See AGENT_BASELINE §8.
62
65
 
63
66
  ## When to Ask the User
64
67
 
65
- You have `AskUserQuestion`. Use it **once, early, on the highest-leverage ambiguity**. Examples:
66
-
67
- - "Should this ship as a library or stay in-tree?"
68
- - "Is the deadline soft (good-enough) or hard (must ship today)?"
69
-
70
- Do not stack questions. One round, max 4 options. If you find yourself asking > 2 questions, route to `@janet` instead.
68
+ Default: **do not ask**. Agents execute routine decisions autonomously per AGENTS.md §"Autonomy and parallelism". Only invoke `AskUserQuestion` when the answer determines the shape of the plan (e.g. library vs in-tree, soft vs hard deadline, public vs internal API). For genuinely ambiguous user input, route to `@janet` once. Never stack questions; never ask permission for a reversible decision; never ask before dispatching parallel subagents whose scopes are already disjoint.
71
69
 
72
70
  ## Tools Available
73
71
 
@@ -2,7 +2,7 @@
2
2
  name: linda
3
3
  description: Linda — QA Reviewer. Audits, criticizes, and corrects implementation plans before execution. Read-only reviewer with no Edit/Write permissions. Use to review a Karen/Carl plan, audit security/correctness, or after a `bizar audit` run.
4
4
  tools: Read, Bash, Glob, Grep, WebFetch, WebSearch, Skill
5
- model: cx/gpt-5.6-sol
5
+ model: cx/gpt-5.6-terra
6
6
  ---
7
7
 
8
8
  You are Linda, the QA Reviewer. You audit plans, code, and configurations before they ship. You have **no Edit or Write permissions** — your only output is feedback.
@@ -2,7 +2,7 @@
2
2
  name: kevin
3
3
  description: Kevin — Support Tech. Read-only browser E2E verification with the official agent-browser CLI or MCP server.
4
4
  tools: Read, Bash, Glob, Grep, WebFetch, WebSearch, Skill
5
- model: bizar/MiniMax-M3
5
+ model: bizar/MiniMax-M2.5
6
6
  ---
7
7
 
8
8
  You are Kevin, the Support Tech. You verify user-facing web behavior with a
@@ -2,7 +2,7 @@
2
2
  name: ria
3
3
  description: Ria — UI/UX Design Specialist. Focuses on good design: typography hierarchy, spacing rhythm, color discipline, motion language, accessibility, anti-slop audits. Differs from @brad (brand identity / DESIGN.md system) — Ria works on the actual UI: components, layout, interaction details. Does not implement code; hands off to @todd/@karen.
4
4
  tools: Read, Edit, Write, Bash, Glob, Grep, WebFetch, WebSearch, Skill
5
- model: cx/gpt-5.6-sol
5
+ model: cx/gpt-5.6-luna
6
6
  isolation: worktree
7
7
  ---
8
8
 
@@ -0,0 +1,8 @@
1
+ ---
2
+ description: Run the canonical Bizar Autopilot workflow from research through verified local delivery.
3
+ argument-hint: "[--workflow <default|plan-build-qa>] <task or outcome>"
4
+ disable-model-invocation: true
5
+ allowed-tools: Skill
6
+ ---
7
+
8
+ Invoke the Skill tool exactly once. For `/bizar-harness:autopilot`, select the installed `bizar-harness:autopilot` skill; for project `/autopilot`, select the installed `autopilot` skill. Pass `$ARGUMENTS` unchanged. Do not invoke this command again or reimplement the skill.
@@ -0,0 +1,8 @@
1
+ ---
2
+ description: Cancel the current durable Bizar workflow without deleting state or unrelated work.
3
+ argument-hint: "[reason]"
4
+ disable-model-invocation: true
5
+ allowed-tools: Skill
6
+ ---
7
+
8
+ Invoke the Skill tool exactly once. For `/bizar-harness:cancel`, select the installed `bizar-harness:cancel` skill; for project `/cancel`, select the installed `cancel` skill. Pass `$ARGUMENTS` unchanged. Do not invoke this command again or reimplement the skill.
@@ -0,0 +1,8 @@
1
+ ---
2
+ description: Run the canonical persistent single-owner Bizar completion loop.
3
+ argument-hint: "<task or failing acceptance criterion>"
4
+ disable-model-invocation: true
5
+ allowed-tools: Skill
6
+ ---
7
+
8
+ Invoke the Skill tool exactly once. For `/bizar-harness:ralph`, select the installed `bizar-harness:ralph` skill; for project `/ralph`, select the installed `ralph` skill. Pass `$ARGUMENTS` unchanged. Do not invoke this command again or reimplement the skill.
@@ -0,0 +1,8 @@
1
+ ---
2
+ description: Run the canonical research-grounded Bizar consensus planning workflow.
3
+ argument-hint: "<task to research and plan>"
4
+ disable-model-invocation: true
5
+ allowed-tools: Skill
6
+ ---
7
+
8
+ Invoke the Skill tool exactly once. For `/bizar-harness:ralplan`, select the installed `bizar-harness:ralplan` skill; for project `/ralplan`, select the installed `ralplan` skill. Pass `$ARGUMENTS` unchanged. Do not invoke this command again or reimplement the skill.
@@ -0,0 +1,8 @@
1
+ ---
2
+ description: Run the canonical bounded Bizar QA and fix cycle.
3
+ argument-hint: "[acceptance path or test scope]"
4
+ disable-model-invocation: true
5
+ allowed-tools: Skill
6
+ ---
7
+
8
+ Invoke the Skill tool exactly once. For `/bizar-harness:ultraqa`, select the installed `bizar-harness:ultraqa` skill; for project `/ultraqa`, select the installed `ultraqa` skill. Pass `$ARGUMENTS` unchanged. Do not invoke this command again or reimplement the skill.
@@ -0,0 +1,8 @@
1
+ ---
2
+ description: Run the canonical Bizar parallel execution workflow for disjoint implementation lanes.
3
+ argument-hint: "<task with independent work lanes>"
4
+ disable-model-invocation: true
5
+ allowed-tools: Skill
6
+ ---
7
+
8
+ Invoke the Skill tool exactly once. For `/bizar-harness:ultrawork`, select the installed `bizar-harness:ultrawork` skill; for project `/ultrawork`, select the installed `ultrawork` skill. Pass `$ARGUMENTS` unchanged. Do not invoke this command again or reimplement the skill.
@@ -0,0 +1,8 @@
1
+ ---
2
+ description: Run canonical functional, security/policy, and quality validation before completion.
3
+ argument-hint: "[change or acceptance criteria]"
4
+ disable-model-invocation: true
5
+ allowed-tools: Skill
6
+ ---
7
+
8
+ Invoke the Skill tool exactly once. For `/bizar-harness:verify`, select the installed `bizar-harness:verify` skill; for project `/verify`, select the installed `verify` skill. Pass `$ARGUMENTS` unchanged. Do not invoke this command again or reimplement the skill.
@@ -0,0 +1,153 @@
1
+ #!/usr/bin/env node
2
+ /** Enforce immutable per-run Agent/model assignments during active workflows. */
3
+
4
+ import { readFileSync } from 'node:fs';
5
+ import { pathToFileURL } from 'node:url';
6
+
7
+ import { WorkflowStateError, getWorkflowState } from '../../cli/core/workflow-state.mjs';
8
+ import { loadModelRouter } from '../../config/agents/model-assignment.mjs';
9
+ import { probeAvailableModels } from '../../cli/commands/workflow.mjs';
10
+
11
+ const BUILT_IN_AGENT_TYPES = new Set([
12
+ 'Explore', 'Plan', 'general-purpose', 'statusline-setup', 'claude-code-guide',
13
+ ]);
14
+
15
+ function deny(reason) {
16
+ return {
17
+ hookSpecificOutput: {
18
+ hookEventName: 'PreToolUse',
19
+ permissionDecision: 'deny',
20
+ permissionDecisionReason: reason,
21
+ },
22
+ };
23
+ }
24
+
25
+ function normalizeEndpoint(value) {
26
+ if (typeof value !== 'string' || value === '' || value.trim() !== value) return null;
27
+ const normalized = value.replace(/\/+$/, '');
28
+ return normalized || null;
29
+ }
30
+
31
+ export async function guardAgentModel(input, options = {}) {
32
+ if (!input || typeof input !== 'object') return {};
33
+ if (input.hook_event_name !== 'PreToolUse' || input.tool_name !== 'Agent') return {};
34
+ const sessionId = typeof input.session_id === 'string' ? input.session_id.trim() : '';
35
+ const toolInput = input.tool_input && typeof input.tool_input === 'object' ? input.tool_input : {};
36
+ const target = String(
37
+ toolInput.subagent_type || toolInput.agent_type || toolInput.agent || toolInput.name || '',
38
+ ).trim();
39
+ if (!target) return sessionId
40
+ ? deny('Bizar Agent dispatch blocked: the active workflow requires an assigned agent target.')
41
+ : {};
42
+ if (BUILT_IN_AGENT_TYPES.has(target)) return {};
43
+ const registryTarget = target.startsWith('bizar-harness:')
44
+ ? target.slice('bizar-harness:'.length)
45
+ : target;
46
+
47
+ let workflow = null;
48
+ if (sessionId) {
49
+ try {
50
+ workflow = getWorkflowState({
51
+ projectRoot: input.cwd || process.cwd(),
52
+ sessionId,
53
+ });
54
+ } catch (error) {
55
+ if (!(error instanceof WorkflowStateError && error.code === 'NOT_FOUND')) {
56
+ return deny(
57
+ `Bizar Agent dispatch blocked because workflow state integrity could not be validated` +
58
+ `${error?.code ? ` (${error.code})` : ''}. Repair or cancel the state before dispatching.`,
59
+ );
60
+ }
61
+ }
62
+ }
63
+
64
+ let assignments;
65
+ let source;
66
+ let probeRegistry;
67
+ if (workflow?.status === 'active') {
68
+ assignments = workflow.assignmentSnapshot?.assignments;
69
+ source = 'active workflow assignment snapshot';
70
+ probeRegistry = {
71
+ gateway: {
72
+ endpoint: workflow.assignmentSnapshot.gatewayEndpoint,
73
+ availabilityProbe: workflow.assignmentSnapshot.availabilityProbe,
74
+ },
75
+ };
76
+ } else {
77
+ let registry;
78
+ try {
79
+ registry = options.registry || loadModelRouter(options.routerPath);
80
+ } catch (error) {
81
+ if (target.startsWith('bizar-harness:') || /^(?:mike|paul|carl|karen|linda|brad|ria|greg|steve|todd|oscar|susan|brenda|pam|janet|kevin)$/.test(registryTarget)) {
82
+ return deny(`Bizar Agent dispatch blocked because the canonical model router is invalid: ${error?.message || String(error)}.`);
83
+ }
84
+ return {};
85
+ }
86
+ assignments = registry.agents;
87
+ source = 'canonical model router';
88
+ probeRegistry = registry;
89
+ }
90
+
91
+ const assignment = assignments?.[registryTarget];
92
+ if (!assignment) {
93
+ if (workflow?.status === 'active' || target.startsWith('bizar-harness:')) {
94
+ return deny(`Bizar Agent dispatch blocked: ${target} is not in the ${source}.`);
95
+ }
96
+ // Non-Bizar custom agent types intentionally outside the registry remain
97
+ // under Claude Code's normal model/permission policy.
98
+ return {};
99
+ }
100
+
101
+ const expectedModel = String(assignment.model || '');
102
+ const explicitModel = typeof toolInput.model === 'string' ? toolInput.model.trim() : '';
103
+ const env = options.env || process.env;
104
+ if (workflow?.status === 'active') {
105
+ const frozenEndpoint = normalizeEndpoint(workflow.assignmentSnapshot.gatewayEndpoint);
106
+ const inferenceEndpoint = normalizeEndpoint(env.ANTHROPIC_BASE_URL);
107
+ if (!inferenceEndpoint) {
108
+ return deny(`Bizar Agent dispatch blocked: active workflow inference requires ANTHROPIC_BASE_URL=${workflow.assignmentSnapshot.gatewayEndpoint}.`);
109
+ }
110
+ if (inferenceEndpoint !== frozenEndpoint) {
111
+ return deny(`Bizar Agent dispatch blocked: ANTHROPIC_BASE_URL does not match frozen workflow gateway ${workflow.assignmentSnapshot.gatewayEndpoint}.`);
112
+ }
113
+ const routerEndpointValue = env.BIZAR_MODEL_ROUTER_URL;
114
+ if (typeof routerEndpointValue === 'string' && routerEndpointValue !== '') {
115
+ const routerEndpoint = normalizeEndpoint(routerEndpointValue);
116
+ if (!routerEndpoint || routerEndpoint !== frozenEndpoint) {
117
+ return deny(`Bizar Agent dispatch blocked: BIZAR_MODEL_ROUTER_URL contradicts frozen workflow gateway ${workflow.assignmentSnapshot.gatewayEndpoint}.`);
118
+ }
119
+ }
120
+ }
121
+ const environmentModel = String(env.CLAUDE_CODE_SUBAGENT_MODEL || '').trim();
122
+ if (explicitModel && explicitModel !== expectedModel) {
123
+ return deny(`Bizar Agent dispatch blocked: ${target} is pinned by the ${source} to ${expectedModel}, not explicit override ${explicitModel}.`);
124
+ }
125
+ if (environmentModel && environmentModel !== expectedModel) {
126
+ return deny(`Bizar Agent dispatch blocked: ${target} is pinned to ${expectedModel}, but CLAUDE_CODE_SUBAGENT_MODEL=${environmentModel}.`);
127
+ }
128
+ let availableModelIds;
129
+ try {
130
+ availableModelIds = options.availableModelIds || await probeAvailableModels({
131
+ registry: probeRegistry,
132
+ fetchImpl: options.fetchImpl,
133
+ timeoutMs: 3_000,
134
+ });
135
+ } catch (error) {
136
+ return deny(`Bizar Agent dispatch blocked: live gateway availability could not be proven for ${expectedModel} (${error?.code || 'GATEWAY_UNAVAILABLE'}).`);
137
+ }
138
+ if (!new Set(availableModelIds).has(expectedModel)) {
139
+ return deny(`Bizar Agent dispatch blocked: live gateway does not report exact assigned model ${expectedModel} for ${target}.`);
140
+ }
141
+ return {};
142
+ }
143
+
144
+ export async function main() {
145
+ let input;
146
+ try { input = JSON.parse(readFileSync(0, 'utf8') || '{}'); } catch {
147
+ process.stdout.write('{}\n');
148
+ return;
149
+ }
150
+ process.stdout.write(`${JSON.stringify(await guardAgentModel(input))}\n`);
151
+ }
152
+
153
+ if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) await main();
@@ -1,48 +1,95 @@
1
1
  #!/usr/bin/env node
2
+ /**
3
+ * .claude/hooks/control-inbox.mjs
4
+ *
5
+ * Bizar OpenKan — UserPromptSubmit / SessionStart hook.
6
+ *
7
+ * On UserPromptSubmit: reads queued control messages and injects them as
8
+ * additional context so the primary session processes durable directives from
9
+ * OpenKan before taking any action.
10
+ *
11
+ * On SessionStart: primes the session with the same control-inbox context so
12
+ * agents see queued messages at session open.
13
+ *
14
+ * Uses import.meta.url + dynamic import() to resolve the sibling CLI module so
15
+ * the hook works regardless of install path (fixes ERR_MODULE_NOT_FOUND after
16
+ * installation when the repo source lived at a non-default location).
17
+ * Lazy import inside the stdin handler avoids top-level-await issues in the
18
+ * transitive dependency chain (control-store.mjs → task-ledger.mjs → better-sqlite3).
19
+ *
20
+ * Claude Code stdin shape (UserPromptSubmit / SessionStart):
21
+ * { session_id, cwd, hook_event_name }
22
+ *
23
+ * Claude Code stdout shape (hookSpecificOutput):
24
+ * { hookSpecificOutput: { hookEventName, additionalContext } }
25
+ */
2
26
 
3
- import { claimControlMessages } from '../../cli/control-store.mjs';
27
+ 'use strict';
4
28
 
5
- let input = {};
6
- try {
7
- let raw = '';
8
- for await (const chunk of process.stdin) raw += chunk;
9
- input = raw.trim() ? JSON.parse(raw) : {};
10
- } catch {
11
- process.stdout.write(JSON.stringify({ continue: true }) + '\n');
12
- process.exit(0);
13
- }
29
+ import { dirname, join } from 'node:path';
30
+ import { fileURLToPath } from 'node:url';
14
31
 
15
- const cwd = input.cwd || process.cwd();
16
- const messages = claimControlMessages(cwd, {
17
- sessionId: input.session_id || '',
18
- agentType: input.agent_type || input.agent_id || '',
19
- });
32
+ const __dirname = dirname(fileURLToPath(import.meta.url));
20
33
 
21
- if (messages.length === 0) {
22
- process.stdout.write(JSON.stringify({ continue: true }) + '\n');
23
- process.exit(0);
24
- }
34
+ async function main() {
35
+ let input = {};
36
+ try {
37
+ let raw = '';
38
+ for await (const chunk of process.stdin) raw += chunk;
39
+ input = raw.trim() ? JSON.parse(raw) : {};
40
+ } catch {
41
+ process.stdout.write(JSON.stringify({ continue: true }) + '\n');
42
+ process.exit(0);
43
+ return;
44
+ }
45
+
46
+ // Dynamic import so the hook resolves correctly regardless of install path.
47
+ // Uses import.meta.url to anchor relative resolution.
48
+ let claimControlMessages;
49
+ try {
50
+ ({ claimControlMessages } = await import(join(__dirname, '..', '..', 'cli', 'control-store.mjs')));
51
+ } catch (err) {
52
+ process.stderr.write(`[bizar.control] WARN: could not load control-store: ${err && err.message ? err.message : String(err)}\n`);
53
+ process.stdout.write(JSON.stringify({ continue: true }) + '\n');
54
+ process.exit(0);
55
+ return;
56
+ }
25
57
 
26
- const context = [
27
- '# Bizar control messages',
28
- '',
29
- 'Process these durable messages in order. They were sent through OpenKan/Bizar.',
30
- '',
31
- ...messages.flatMap((message) => [
32
- `## Message ${message.id}`,
33
- `From: ${message.from}`,
34
- message.taskId ? `Task: ${message.taskId}` : null,
58
+ const cwd = input.cwd || process.cwd();
59
+ const messages = claimControlMessages(cwd, {
60
+ sessionId: input.session_id || '',
61
+ agentType: input.agent_type || input.agent_id || '',
62
+ });
63
+
64
+ if (messages.length === 0) {
65
+ process.stdout.write(JSON.stringify({ continue: true }) + '\n');
66
+ process.exit(0);
67
+ return;
68
+ }
69
+
70
+ const context = [
71
+ '# Bizar control messages',
35
72
  '',
36
- message.text,
73
+ 'Process these durable messages in order. They were sent through OpenKan/Bizar.',
37
74
  '',
38
- ].filter(Boolean)),
39
- ].join('\n');
75
+ ...messages.flatMap((message) => [
76
+ `## Message ${message.id}`,
77
+ `From: ${message.from}`,
78
+ message.taskId ? `Task: ${message.taskId}` : null,
79
+ '',
80
+ message.text,
81
+ '',
82
+ ].filter(Boolean)),
83
+ ].join('\n');
40
84
 
41
- process.stdout.write(JSON.stringify({
42
- continue: true,
43
- hookSpecificOutput: {
44
- hookEventName: input.hook_event_name || 'SessionStart',
45
- additionalContext: context,
46
- },
47
- }) + '\n');
85
+ process.stdout.write(JSON.stringify({
86
+ continue: true,
87
+ hookSpecificOutput: {
88
+ hookEventName: input.hook_event_name || 'SessionStart',
89
+ additionalContext: context,
90
+ },
91
+ }) + '\n');
92
+ process.exit(0);
93
+ }
48
94
 
95
+ main();