@polderlabs/bizar 10.31.0 → 10.33.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/AGENTS.md +37 -53
- package/README.md +14 -13
- package/cli/banner.mjs +1 -1
- package/cli/bin.mjs +22 -1
- package/cli/capabilities.mjs +21 -0
- package/cli/commands/ao.mjs +15 -4
- package/cli/commands/improve-proposal.mjs +12 -4
- package/cli/commands/improve.mjs +44 -13
- package/cli/commands/install.mjs +44 -28
- package/cli/commands/learn.mjs +23 -7
- package/cli/commands/openkan.mjs +1 -1
- package/cli/commands/openwolf-memory-command.mjs +28 -0
- package/cli/commands/release-provenance.mjs +35 -20
- package/cli/commands/util.mjs +10 -9
- package/cli/commands/worker.mjs +3 -0
- package/cli/core/backup-store.mjs +51 -19
- package/cli/core/runtime-manifest.mjs +31 -0
- package/cli/doctor.mjs +46 -1
- package/cli/execution-context.mjs +56 -0
- package/cli/init.mjs +46 -4
- package/cli/install/banner.mjs +1 -1
- package/cli/install/index.mjs +17 -18
- package/cli/install/interactive-setup.mjs +7 -4
- package/cli/install/paths.mjs +1 -1
- package/cli/openkan.mjs +8 -11
- package/cli/openwolf-memory.mjs +110 -0
- package/cli/openwolf.mjs +106 -0
- package/cli/policies.mjs +43 -0
- package/cli/provision.mjs +269 -71
- package/cli/repair.mjs +19 -4
- package/cli/uninstall-support.mjs +8 -0
- package/cli/uninstall.mjs +48 -0
- package/cli/update.mjs +44 -0
- package/config/ao/worker-rules.md +15 -0
- package/config/claude/CLAUDE.md +33 -53
- package/config/claude/agents/_shared/AGENT_BASELINE.md +21 -12
- package/config/claude/agents/bizar-harness-optimizer.md +1 -1
- package/config/claude/agents/office-manager.md +55 -69
- package/config/claude/agents/support-tech.md +4 -3
- package/config/claude/commands/bizar.md +3 -3
- package/config/claude/commands/plow-through.md +11 -9
- package/config/claude/commands/team.md +2 -2
- package/config/claude/compatibility.mjs +22 -0
- package/config/claude/hooks/git-workflow-guard.mjs +7 -7
- package/config/claude/hooks/permission-request.mjs +14 -182
- package/config/claude/hooks/persistent-mode.mjs +1 -1
- package/config/claude/hooks/sessionstart-prime.mjs +29 -17
- package/config/claude/hooks/worker-suggest.mjs +7 -7
- package/config/claude/settings.json +3 -5
- package/config/compatibility-manifest.json +23 -0
- package/config/policy/AGENT_BEHAVIOR.md +18 -0
- package/config/skills/agent-browser/SKILL.md +4 -4
- package/config/skills/autopilot/SKILL.md +3 -3
- package/config/skills/bizar/SKILL.md +9 -3
- package/config/skills/bizplan/SKILL.md +1 -1
- package/config/skills/bizplan/references/pre-mortem.md +1 -1
- package/config/skills/clean-gone-branches/SKILL.md +1 -1
- package/config/skills/create-pr/SKILL.md +2 -2
- package/config/skills/deep-interview/SKILL.md +1 -1
- package/config/skills/harness-engineering/SKILL.md +3 -3
- package/config/skills/resolve-pr-comments/SKILL.md +1 -1
- package/config/skills/skillopt/SKILL.md +2 -2
- package/config/skills/ultracode/SKILL.md +1 -1
- package/install.sh +34 -36
- package/package.json +8 -5
- package/packages/sdk/dist/federation/index.d.ts +5 -0
- package/packages/sdk/dist/federation/index.js +31 -4
- package/packages/sdk/dist/federation/pii.js +14 -9
- package/packages/sdk/dist/federation/policy.d.ts +1 -1
- package/packages/sdk/dist/federation/policy.js +3 -0
- package/packages/sdk/dist/index.d.ts +2 -0
- package/packages/sdk/dist/index.js +4 -0
- package/packages/sdk/dist/mcp/bin.js +2 -25
- package/packages/sdk/dist/mcp/server.d.ts +4 -0
- package/packages/sdk/dist/mcp/server.js +53 -10
- package/packages/sdk/dist/mcp/stdio.d.ts +6 -0
- package/packages/sdk/dist/mcp/stdio.js +66 -0
- package/packages/sdk/dist/policy/action-policy.d.ts +13 -0
- package/packages/sdk/dist/policy/action-policy.js +8 -0
- package/packages/sdk/dist/policy/authority.d.ts +2 -0
- package/packages/sdk/dist/policy/authority.js +2 -0
- package/packages/sdk/dist/policy/capability-registry.d.ts +46 -0
- package/packages/sdk/dist/policy/capability-registry.js +24 -0
- package/packages/sdk/dist/policy/completion-policy.d.ts +42 -0
- package/packages/sdk/dist/policy/completion-policy.js +17 -0
- package/packages/sdk/dist/policy/evidence-policy.d.ts +2 -0
- package/packages/sdk/dist/policy/evidence-policy.js +2 -0
- package/packages/sdk/dist/policy/execution-context.d.ts +28 -0
- package/packages/sdk/dist/policy/execution-context.js +43 -0
- package/packages/sdk/dist/policy/index.d.ts +10 -0
- package/packages/sdk/dist/policy/index.js +10 -0
- package/packages/sdk/dist/policy/provision-policy.d.ts +9 -0
- package/packages/sdk/dist/policy/provision-policy.js +8 -0
- package/packages/sdk/dist/policy/retry-policy.d.ts +10 -0
- package/packages/sdk/dist/policy/retry-policy.js +12 -0
- package/packages/sdk/dist/policy/tool-behavior.d.ts +37 -0
- package/packages/sdk/dist/policy/tool-behavior.js +19 -0
- package/packages/sdk/dist/release/known-good-releases.d.ts +2 -1
- package/packages/sdk/dist/release/known-good-releases.js +4 -16
- package/packages/sdk/dist/release/signature.js +13 -2
- package/packages/sdk/dist/version.d.ts +1 -1
- package/packages/sdk/dist/version.js +1 -1
- package/packages/sdk/package.json +1 -1
package/AGENTS.md
CHANGED
|
@@ -32,7 +32,9 @@ make session-end # lifecycle compatibility target
|
|
|
32
32
|
- **MUST** verify evidence before claiming completion.
|
|
33
33
|
- **MUST NOT** commit `console.log`, `debugger`, `.only()`, credentials, generated secrets, or runtime logs.
|
|
34
34
|
- **MUST NOT** use a persistent Claude daemon. Claude Code and the Agent SDK run in-process; background work uses Claude Code's Agent tool.
|
|
35
|
-
- **MUST NOT** rebase or force-push
|
|
35
|
+
- **MUST NOT** rebase or force-push unless an explicit operator restriction or
|
|
36
|
+
host/runtime contract forbids it; Bizar itself does not gate high-impact
|
|
37
|
+
actions by impact.
|
|
36
38
|
|
|
37
39
|
### Worktree discipline
|
|
38
40
|
|
|
@@ -58,16 +60,16 @@ edit, test, and iterate without pausing for routine decisions. Routine
|
|
|
58
60
|
decisions (file layout, naming, scope of a single commit, choosing between
|
|
59
61
|
two equivalent stdlib calls, picking a verification command from the Makefile,
|
|
60
62
|
or completing an OpenKan task after `make check` is green) do NOT require
|
|
61
|
-
human
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
63
|
+
human coordination and MUST NOT trigger a permission handoff. PreToolUse hooks may
|
|
64
|
+
classify high-impact actions and add evidence guidance, but they must not deny
|
|
65
|
+
or ask solely because an action is externally visible, irreversible, or
|
|
66
|
+
powerful. Invalid target/schema/state is a typed contract error, not a
|
|
67
|
+
permission handoff.
|
|
68
|
+
|
|
69
|
+
Mike performs bounded read-only orientation, then chooses the smallest capable
|
|
70
|
+
execution surface for the requested outcome. Direct work is the default when
|
|
71
|
+
one agent can safely complete and verify the task; native Agent teams are used
|
|
72
|
+
only when independent lanes materially improve throughput or correctness. Ask one
|
|
71
73
|
concise clarification question only when a material choice, acceptance
|
|
72
74
|
criterion, safety boundary, or unresolved constraint would change the work;
|
|
73
75
|
otherwise continue autonomously. The team is host-side state under
|
|
@@ -86,46 +88,25 @@ default. If no enabled configured candidate exists, dispatch fails with an
|
|
|
86
88
|
actionable configuration error. Bizar never retries a failed dispatch by cycling
|
|
87
89
|
aliases, providers, or tiers.
|
|
88
90
|
|
|
89
|
-
The authoritative
|
|
91
|
+
The authoritative default is maximum autonomy; pushes, pull-request
|
|
90
92
|
mutations, releases, package publication, deployments, production/shared-
|
|
91
93
|
infrastructure writes, credential changes, public exposure, irreversible
|
|
92
|
-
destruction
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
> `permissions.allow` so subagents do not prompt for commits, pushes, PRs,
|
|
103
|
-
> or deploys. Operators who want HITL back can move the following exact
|
|
104
|
-
> patterns from `permissions.allow` into `permissions.ask` in their local
|
|
105
|
-
> `~/.claude/settings.json` override:
|
|
106
|
-
>
|
|
107
|
-
> - `Bash(git push *)`
|
|
108
|
-
> - `Bash(git -C * push *)`
|
|
109
|
-
> - `Bash(git --git-dir=* push *)`
|
|
110
|
-
> - `Bash(git push --force *)`
|
|
111
|
-
> - `Bash(git push -f *)`
|
|
112
|
-
> - `Bash(git rebase *)`
|
|
113
|
-
> - `Bash(gh pr create *)`
|
|
114
|
-
> - `Bash(gh pr merge *)`
|
|
115
|
-
> - `Bash(gh release create *)`
|
|
116
|
-
> - `Bash(npm publish *)`
|
|
117
|
-
> - `Bash(bun publish *)`
|
|
118
|
-
> - `Bash(pnpm publish *)`
|
|
119
|
-
> - `Bash(vercel deploy *)`
|
|
120
|
-
> - `Bash(wrangler deploy *)`
|
|
121
|
-
> - `Bash(flyctl deploy *)`
|
|
94
|
+
destruction proceed when explicitly requested and contract-valid. Stronger
|
|
95
|
+
evidence and rollback handling are required; a permission prompt is not.
|
|
96
|
+
|
|
97
|
+
The default maximum-autonomy profile is implemented by the host runtime;
|
|
98
|
+
`permission-request.mjs` is a fail-open compatibility hook. The workflow and
|
|
99
|
+
PreToolUse hooks may emit advisory evidence guidance but never create a Bizar
|
|
100
|
+
approval gate.
|
|
101
|
+
Historical F-176 approval-floor wording is retained only in superseded audit
|
|
102
|
+
records. Operators may configure their host independently, but Bizar does not
|
|
103
|
+
reintroduce an impact-based or role-based gate.
|
|
122
104
|
|
|
123
105
|
> Note: `disableAutoCompact: false` is shipped by default so Claude Code can
|
|
124
106
|
> compact automatically before the context limit. Hook `precompact-priorities.sh`
|
|
125
107
|
> snapshots bounded state and preserves evidence and decisions on compaction.
|
|
126
|
-
Local
|
|
127
|
-
|
|
128
|
-
hook chain.
|
|
108
|
+
Local and external mutations follow the same action-contract path; hooks add
|
|
109
|
+
evidence guidance only.
|
|
129
110
|
|
|
130
111
|
Agents fetch current official documentation via WebSearch + WebFetch before
|
|
131
112
|
acting on an external API, library, framework, CLI, configuration format, or
|
|
@@ -140,14 +121,11 @@ The autonomy and approval policy above governs this execution model. The project
|
|
|
140
121
|
Every non-empty primary request enters Bizar through `office-manager` (`@mike`).
|
|
141
122
|
The installer sets Claude Code's global `agent` setting to Mike's frontmatter
|
|
142
123
|
name (`mike`),
|
|
143
|
-
and the routing hook supplies the
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
default Agent team and continues autonomously. It asks one concise
|
|
124
|
+
and the routing hook supplies the coordination policy. Mike gathers bounded
|
|
125
|
+
read-only context, selects direct execution or a narrowly scoped Agent team
|
|
126
|
+
based on coordination value, and continues autonomously. It asks one concise
|
|
147
127
|
clarification only when a material choice or unresolved constraint would
|
|
148
|
-
change the work.
|
|
149
|
-
single Agents and native workflows are explicit or resumed modes. Mike owns
|
|
150
|
-
integration and final verification.
|
|
128
|
+
change the work. Mike owns integration and final verification.
|
|
151
129
|
A Bizar custom agent already executing its assigned role does not recursively
|
|
152
130
|
dispatch itself.
|
|
153
131
|
|
|
@@ -201,4 +179,10 @@ not a general note vault, semantic search service, or knowledge-base API.
|
|
|
201
179
|
2. Documentation and OpenKan task/plan/PRD state describe the actual code.
|
|
202
180
|
3. `make verify-removed-surfaces`, `make verify-repo-structure`, `make check-arch`, `make test`, `make e2e`, `make clean-check`, and `make check` pass as applicable.
|
|
203
181
|
4. The OpenKan task records fresh verification evidence and no required work remains.
|
|
204
|
-
5. `/simplify` reviews the staged diff before the
|
|
182
|
+
5. `/simplify` reviews the staged diff before the evidence-backed commit.
|
|
183
|
+
|
|
184
|
+
<!-- openwolf:begin -->
|
|
185
|
+
# OpenWolf
|
|
186
|
+
|
|
187
|
+
This project uses OpenWolf for context management. Read and follow .wolf/OPENWOLF.md at session start. Check .wolf/cerebrum.md before generating code. Grep .wolf/anatomy.md for a file's path before reading it (never read the whole index).
|
|
188
|
+
<!-- openwolf:end -->
|
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<div align="center">
|
|
2
2
|
|
|
3
|
-
<img src="docs/assets/bizar-banner.svg" alt="Bizar:
|
|
3
|
+
<img src="docs/assets/bizar-banner.svg" alt="Bizar: maximum autonomy for Agent Orchestrator" width="100%" />
|
|
4
4
|
|
|
5
5
|
[](https://www.npmjs.com/package/@polderlabs/bizar)
|
|
6
6
|
[](LICENSE)
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|

|
|
10
10
|
[](https://www.npmjs.com/package/@polderlabs/openkan)
|
|
11
11
|
|
|
12
|
-
###
|
|
12
|
+
### Maximum autonomy for Agent Orchestrator workers
|
|
13
13
|
|
|
14
14
|
Run focused Codex workers under Agent Orchestrator. Bizar supplies their
|
|
15
15
|
repository policy, guardrails, skills, and verification evidence.
|
|
@@ -27,9 +27,10 @@ each worker's implementation and verification discipline explicit. AO owns
|
|
|
27
27
|
multi-agent coordination, worktrees, branches, PR/review/CI feedback, previews,
|
|
28
28
|
and browser state; Bizar does not duplicate those surfaces.
|
|
29
29
|
|
|
30
|
-
It
|
|
31
|
-
AO model choices live in the registered
|
|
32
|
-
Claude aliases live in
|
|
30
|
+
It records evidence and rollback context for high-impact actions while keeping
|
|
31
|
+
execution autonomous by default. AO model choices live in the registered
|
|
32
|
+
project's AO configuration. Standalone Claude aliases live in global Claude
|
|
33
|
+
configuration.
|
|
33
34
|
|
|
34
35
|
| You want | Bizar provides |
|
|
35
36
|
| --- | --- |
|
|
@@ -181,10 +182,10 @@ risk. It does not create parallel workers merely to look busy.
|
|
|
181
182
|
| --- | --- |
|
|
182
183
|
| Research, planning, implementation, review, verification | Architecture, accessibility, security, tests, documentation, performance, build repair, operations, and domain analysis |
|
|
183
184
|
|
|
184
|
-
##
|
|
185
|
+
## Evidence that stays in the loop
|
|
185
186
|
|
|
186
|
-
Bizar
|
|
187
|
-
|
|
187
|
+
Bizar executes valid operations autonomously and records stronger evidence and
|
|
188
|
+
rollback context when the effect is consequential.
|
|
188
189
|
|
|
189
190
|
| Category | Default behavior |
|
|
190
191
|
| --- | --- |
|
|
@@ -192,12 +193,12 @@ for consequential actions.
|
|
|
192
193
|
| Parallel code changes | Uses isolated worktrees and scoped task ownership |
|
|
193
194
|
| Ambiguous material design choice | Asks one concise clarification before execution |
|
|
194
195
|
| Commit | Locally allowed, with a fresh simplify review reminder |
|
|
195
|
-
| Push, PR mutation, release, publish, deploy |
|
|
196
|
-
| Rebase, force-push, broad destructive commands |
|
|
196
|
+
| Push, PR mutation, release, publish, deploy | Executes when requested and contract-valid; records evidence |
|
|
197
|
+
| Rebase, force-push, broad destructive commands | Executes when requested and contract-valid; advisory context only |
|
|
197
198
|
|
|
198
|
-
The goal is
|
|
199
|
-
|
|
200
|
-
|
|
199
|
+
The goal is clear request fidelity: invalid targets, schemas, signatures, and
|
|
200
|
+
lifecycle states fail deterministically, while explicit operator restrictions
|
|
201
|
+
remain binding.
|
|
201
202
|
|
|
202
203
|
## What gets installed
|
|
203
204
|
|
package/cli/banner.mjs
CHANGED
|
@@ -14,7 +14,7 @@ export function showBanner() {
|
|
|
14
14
|
console.log(chalk.hex('#6366f1').bold(RUNE_HELM));
|
|
15
15
|
console.log(chalk.hex('#a855f7')(' Guarded autonomous agent system for Claude Code'));
|
|
16
16
|
console.log();
|
|
17
|
-
console.log(chalk.dim(' 84 agents ·
|
|
17
|
+
console.log(chalk.dim(' 84 agents · maximum autonomy · evidence verification · MCP · Skills CLI'));
|
|
18
18
|
console.log();
|
|
19
19
|
}
|
|
20
20
|
|
package/cli/bin.mjs
CHANGED
|
@@ -103,10 +103,12 @@ function showHelp() {
|
|
|
103
103
|
update Update Claude Code, Bizar, and the SDK
|
|
104
104
|
doctor Check the BizarHarness install for health issues
|
|
105
105
|
repair Fix common install issues
|
|
106
|
+
uninstall Remove Bizar-owned files safely (dry-run supported)
|
|
106
107
|
heads-up <subcommand> Manage pre-push / pre-release heads-ups
|
|
107
108
|
browser Install, update, and verify agent-browser
|
|
108
109
|
artifact Toggle global browser completion artifacts
|
|
109
|
-
learn Manage bounded global preferences and project
|
|
110
|
+
learn Manage bounded global preferences and OpenWolf project memory
|
|
111
|
+
memory Manage OpenWolf project memory/context (current project by default)
|
|
110
112
|
tools Inventory installed agents, skills, hooks, commands, and MCP tools
|
|
111
113
|
upgrade-defaults Reapply safe Bizar-owned Claude defaults
|
|
112
114
|
backup Create / list / verify / delete backups of BizarHarness state
|
|
@@ -266,6 +268,18 @@ async function main() {
|
|
|
266
268
|
break;
|
|
267
269
|
}
|
|
268
270
|
|
|
271
|
+
case 'uninstall': {
|
|
272
|
+
const { runUninstall, showUninstallHelp } = await import('./uninstall.mjs');
|
|
273
|
+
if (isHelpRequest) showUninstallHelp();
|
|
274
|
+
else {
|
|
275
|
+
const result = await runUninstall({ dryRun: cmdArgs.includes('--dry-run'), keepState: cmdArgs.includes('--keep-state'), purgeOpenWolfRuntime: cmdArgs.includes('--purge-openwolf-runtime') });
|
|
276
|
+
for (const path of [...result.removed, ...result.restored]) console.log(` ${result.dryRun ? 'would change' : 'changed'} ${path}`);
|
|
277
|
+
for (const path of result.conflicts) console.log(` ! preserved modified file ${path}`);
|
|
278
|
+
if (!result.ok) process.exit(EXIT_ERROR);
|
|
279
|
+
}
|
|
280
|
+
break;
|
|
281
|
+
}
|
|
282
|
+
|
|
269
283
|
|
|
270
284
|
case 'validate': {
|
|
271
285
|
const mod = await importCommand('validate');
|
|
@@ -529,6 +543,13 @@ async function main() {
|
|
|
529
543
|
break;
|
|
530
544
|
}
|
|
531
545
|
|
|
546
|
+
case 'memory': {
|
|
547
|
+
const mod = await importCommand('openwolf-memory-command');
|
|
548
|
+
if (!mod) { process.exit(EXIT_ERROR); return; }
|
|
549
|
+
await mod.run(cmd, cmdArgs, isHelpRequest);
|
|
550
|
+
break;
|
|
551
|
+
}
|
|
552
|
+
|
|
532
553
|
case 'tools':
|
|
533
554
|
case 'upgrade-defaults': {
|
|
534
555
|
const mod = await importCommand(cmd);
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { BIZAR_HOME } from './provision.mjs';
|
|
4
|
+
|
|
5
|
+
export function readCapabilitySnapshot() {
|
|
6
|
+
try { return JSON.parse(readFileSync(join(BIZAR_HOME(), 'capabilities.json'), 'utf8')); } catch { return null; }
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function selectAvailableCapabilities(intent, snapshot = readCapabilitySnapshot()) {
|
|
10
|
+
if (!snapshot) return [];
|
|
11
|
+
const terms = String(intent || '').toLowerCase().split(/[^a-z0-9]+/).filter(Boolean);
|
|
12
|
+
return (snapshot.capabilities || [])
|
|
13
|
+
.filter((capability) => capability.availability === 'available')
|
|
14
|
+
.map((capability) => ({ capability, score: terms.reduce((score, term) => {
|
|
15
|
+
const haystack = `${capability.name} ${capability.description || ''} ${(capability.tags || []).join(' ')}`.toLowerCase();
|
|
16
|
+
return score + (haystack.includes(term) ? 1 : 0);
|
|
17
|
+
}, 0) }))
|
|
18
|
+
.filter(({ score }) => score > 0)
|
|
19
|
+
.sort((a, b) => b.score - a.score)
|
|
20
|
+
.map(({ capability }) => capability);
|
|
21
|
+
}
|
package/cli/commands/ao.mjs
CHANGED
|
@@ -10,7 +10,7 @@ import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
|
10
10
|
import { fileURLToPath } from 'node:url';
|
|
11
11
|
import { basename, dirname, resolve } from 'node:path';
|
|
12
12
|
|
|
13
|
-
export const AO_RULES_FILE = '.ao/bizar-
|
|
13
|
+
export const AO_RULES_FILE = '.ao/bizar-managed-rules.md';
|
|
14
14
|
export const AO_ORCHESTRATOR_RULES = 'Use AO for coordination and Bizar as the Codex worker harness. Spawn focused AO workers for implementation; workers own changes, verification, commits, and PR follow-up.';
|
|
15
15
|
const WORKER_RULES_TEMPLATE = resolve(dirname(fileURLToPath(import.meta.url)), '../../config/ao/worker-rules.md');
|
|
16
16
|
|
|
@@ -28,6 +28,8 @@ export function parseAoArgs(args = []) {
|
|
|
28
28
|
project: optionValue(args, '--project'),
|
|
29
29
|
model: optionValue(args, '--model'),
|
|
30
30
|
permissions: optionValue(args, '--permissions'),
|
|
31
|
+
workerAgent: optionValue(args, '--worker-agent'),
|
|
32
|
+
orchestratorAgent: optionValue(args, '--orchestrator-agent'),
|
|
31
33
|
help: args.includes('--help') || args.includes('-h') || first === 'help',
|
|
32
34
|
};
|
|
33
35
|
}
|
|
@@ -42,8 +44,12 @@ export function defaultProjectId(cwd) {
|
|
|
42
44
|
}
|
|
43
45
|
|
|
44
46
|
export function configuredProjectConfig(current = {}, options = {}) {
|
|
45
|
-
const worker = { ...(current.worker || {})
|
|
46
|
-
const orchestrator = { ...(current.orchestrator || {})
|
|
47
|
+
const worker = { ...(current.worker || {}) };
|
|
48
|
+
const orchestrator = { ...(current.orchestrator || {}) };
|
|
49
|
+
if (!worker.agent) worker.agent = 'codex';
|
|
50
|
+
if (!orchestrator.agent) orchestrator.agent = 'codex';
|
|
51
|
+
if (options.workerAgent) worker.agent = options.workerAgent;
|
|
52
|
+
if (options.orchestratorAgent) orchestrator.agent = options.orchestratorAgent;
|
|
47
53
|
const agentConfig = { ...(current.agentConfig || {}) };
|
|
48
54
|
if (options.model) agentConfig.model = options.model;
|
|
49
55
|
if (options.permissions) agentConfig.permissions = options.permissions;
|
|
@@ -165,7 +171,9 @@ export function setupAo(options = {}) {
|
|
|
165
171
|
projectId ||= defaultProjectId(cwd);
|
|
166
172
|
|
|
167
173
|
if (!projectDetails) {
|
|
168
|
-
const
|
|
174
|
+
const addArgs = ['project', 'add', '--path', cwd, '--id', projectId];
|
|
175
|
+
addArgs.push('--worker-agent', options.workerAgent || 'codex', '--orchestrator-agent', options.orchestratorAgent || 'codex');
|
|
176
|
+
const added = runAo(addArgs, options);
|
|
169
177
|
if (!added.ok) return added;
|
|
170
178
|
const details = runAo(['project', 'get', projectId, '--json'], options);
|
|
171
179
|
if (!details.ok) return details;
|
|
@@ -211,6 +219,9 @@ export function run(args = [], options = {}) {
|
|
|
211
219
|
return true;
|
|
212
220
|
}
|
|
213
221
|
try {
|
|
222
|
+
if (parsed.subcommand === 'setup' && isAoSession(options.env || process.env) && !options.allowNested) {
|
|
223
|
+
throw new Error('AO_OWNED_OPERATION: AO setup cannot create competing lifecycle state from an AO worker');
|
|
224
|
+
}
|
|
214
225
|
let result;
|
|
215
226
|
if (parsed.subcommand === 'setup') result = setupAo({ ...options, ...parsed });
|
|
216
227
|
else if (parsed.subcommand === 'check') result = checkAo(options);
|
|
@@ -37,7 +37,7 @@ import { createHash } from 'node:crypto';
|
|
|
37
37
|
* originalSha256: string,
|
|
38
38
|
* find: string,
|
|
39
39
|
* newText: string,
|
|
40
|
-
* verification: { command
|
|
40
|
+
* verification: { command?: string, argv?: string[], cwd?: string, timeoutMs?: number, expectedExitCode?: number },
|
|
41
41
|
* rollbackPlan: { kind: 'replace-back', note: string } | { kind: 'manual', note: string, manualCommand?: string },
|
|
42
42
|
* reason: string,
|
|
43
43
|
* createdAt: string,
|
|
@@ -98,8 +98,16 @@ export function validateProposal(raw) {
|
|
|
98
98
|
if (raw.find === raw.newText) {
|
|
99
99
|
throw new TypeError('Proposal.newText must differ from Proposal.find');
|
|
100
100
|
}
|
|
101
|
-
if (typeof raw.verification !== 'object'
|
|
102
|
-
|
|
101
|
+
if (typeof raw.verification !== 'object'
|
|
102
|
+
|| (!Array.isArray(raw.verification.argv) && (typeof raw.verification.command !== 'string' || raw.verification.command.length === 0))) {
|
|
103
|
+
throw new TypeError('Proposal.verification requires a non-empty argv array or command string');
|
|
104
|
+
}
|
|
105
|
+
if (Array.isArray(raw.verification.argv)
|
|
106
|
+
&& (raw.verification.argv.length === 0 || raw.verification.argv.some((arg) => typeof arg !== 'string' || arg.length === 0))) {
|
|
107
|
+
throw new TypeError('Proposal.verification.argv must contain non-empty strings');
|
|
108
|
+
}
|
|
109
|
+
if (typeof raw.verification.command === 'string' && /(?:^|[^\\])(?:;|&&|\|\||`|\$\()/.test(raw.verification.command) && !Array.isArray(raw.verification.argv)) {
|
|
110
|
+
throw new TypeError('Proposal.verification.command contains shell syntax; use verification.argv');
|
|
103
111
|
}
|
|
104
112
|
if (typeof raw.rollbackPlan !== 'object' || (raw.rollbackPlan.kind !== 'replace-back' && raw.rollbackPlan.kind !== 'manual')) {
|
|
105
113
|
throw new TypeError('Proposal.rollbackPlan.kind must be "replace-back" or "manual"');
|
|
@@ -152,4 +160,4 @@ export function planRollback(proposal, currentBytes) {
|
|
|
152
160
|
return { kind: 'replace-back', newBytes: currentBytes.replace(proposal.newText, proposal.find) };
|
|
153
161
|
}
|
|
154
162
|
return { kind: 'manual', manualCommand: proposal.rollbackPlan.manualCommand };
|
|
155
|
-
}
|
|
163
|
+
}
|
package/cli/commands/improve.mjs
CHANGED
|
@@ -56,7 +56,7 @@ import {
|
|
|
56
56
|
statSync,
|
|
57
57
|
writeFileSync,
|
|
58
58
|
} from 'node:fs';
|
|
59
|
-
import { isAbsolute, join, resolve } from 'node:path';
|
|
59
|
+
import { dirname, isAbsolute, join, resolve } from 'node:path';
|
|
60
60
|
import { spawnSync } from 'node:child_process';
|
|
61
61
|
import { ensureSecureDir } from './secure-dir.mjs';
|
|
62
62
|
import {
|
|
@@ -145,19 +145,57 @@ function readCurrentFile({ file, cwd }) {
|
|
|
145
145
|
return { abs, bytes: readFileSync(abs, 'utf8') };
|
|
146
146
|
}
|
|
147
147
|
|
|
148
|
+
/** Parse a deliberately small argv form without invoking a shell. */
|
|
149
|
+
export function parseSimpleArgv(command) {
|
|
150
|
+
if (typeof command !== 'string' || !command.trim()) throw new TypeError('verification command must be non-empty');
|
|
151
|
+
if (/(?:;|&&|\|\||`|\$\(|>|<|\n|\r)/.test(command)) {
|
|
152
|
+
throw new TypeError('verification command contains shell syntax; use argv');
|
|
153
|
+
}
|
|
154
|
+
const argv = [];
|
|
155
|
+
let token = '';
|
|
156
|
+
let quote = null;
|
|
157
|
+
let escaped = false;
|
|
158
|
+
for (const char of command.trim()) {
|
|
159
|
+
if (escaped) { token += char; escaped = false; continue; }
|
|
160
|
+
if (char === '\\' && quote !== "'") { escaped = true; continue; }
|
|
161
|
+
if (quote) {
|
|
162
|
+
if (char === quote) quote = null;
|
|
163
|
+
else token += char;
|
|
164
|
+
continue;
|
|
165
|
+
}
|
|
166
|
+
if (char === '"' || char === "'") { quote = char; continue; }
|
|
167
|
+
if (/\s/.test(char)) {
|
|
168
|
+
if (token) { argv.push(token); token = ''; }
|
|
169
|
+
continue;
|
|
170
|
+
}
|
|
171
|
+
token += char;
|
|
172
|
+
}
|
|
173
|
+
if (escaped || quote || !token && argv.length === 0) throw new TypeError('verification command has unterminated quoting');
|
|
174
|
+
if (token) argv.push(token);
|
|
175
|
+
return argv;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function runVerification(verification, cwd) {
|
|
179
|
+
const verifyCwd = verification.cwd || cwd;
|
|
180
|
+
const timeout = Number(verification.timeoutMs) || 60_000;
|
|
181
|
+
const argv = Array.isArray(verification.argv) ? verification.argv : parseSimpleArgv(verification.command);
|
|
182
|
+
return { result: spawnSync(argv[0], argv.slice(1), { cwd: verifyCwd, encoding: 'utf8', timeout, shell: false }), cwd: verifyCwd };
|
|
183
|
+
}
|
|
184
|
+
|
|
148
185
|
function doPropose({ flags, cwd }) {
|
|
149
186
|
if (!flags.file || !flags.find || !flags.new || !flags.verify) {
|
|
150
187
|
process.stderr.write('usage: bizar improve propose --file <path> --find <substr> --new <text> --verify <cmd> [--reason <why>]\n');
|
|
151
188
|
process.exit(2);
|
|
152
189
|
}
|
|
153
190
|
const { abs, bytes } = readCurrentFile({ file: flags.file, cwd });
|
|
191
|
+
const argv = parseSimpleArgv(flags.verify);
|
|
154
192
|
const proposal = {
|
|
155
193
|
id: newProposalId({ targetFile: abs, cwd }),
|
|
156
194
|
targetFile: abs,
|
|
157
195
|
originalSha256: sha256Text(bytes),
|
|
158
196
|
find: flags.find,
|
|
159
197
|
newText: flags.new,
|
|
160
|
-
verification: { command: flags.verify, cwd, expectedExitCode: 0 },
|
|
198
|
+
verification: { command: flags.verify, argv, cwd, expectedExitCode: 0 },
|
|
161
199
|
rollbackPlan: { kind: 'replace-back', note: `Reverse the find/newText swap on ${abs}` },
|
|
162
200
|
reason: String(flags.reason ?? '(no reason supplied)'),
|
|
163
201
|
createdAt: new Date().toISOString(),
|
|
@@ -218,12 +256,7 @@ function doRun({ flags, cwd }) {
|
|
|
218
256
|
writeFileSync(abs, plan.newBytes, { mode: statSync(abs).mode & 0o777 });
|
|
219
257
|
|
|
220
258
|
// Verify.
|
|
221
|
-
const verifyCwd = proposal.verification
|
|
222
|
-
const verify = spawnSync('sh', ['-c', proposal.verification.command], {
|
|
223
|
-
cwd: verifyCwd,
|
|
224
|
-
encoding: 'utf8',
|
|
225
|
-
timeout: Number(proposal.verification.timeoutMs) || 60_000,
|
|
226
|
-
});
|
|
259
|
+
const { result: verify, cwd: verifyCwd } = runVerification(proposal.verification, cwd);
|
|
227
260
|
const verifyExit = verify.status ?? -1;
|
|
228
261
|
const verifyStdoutSha = sha256Text(verify.stdout ?? '');
|
|
229
262
|
const verifyStderrSha = sha256Text(verify.stderr ?? '');
|
|
@@ -245,6 +278,7 @@ function doRun({ flags, cwd }) {
|
|
|
245
278
|
rolledBackSha256: rollback.kind === 'replace-back' ? sha256Text(rollback.newBytes) : null,
|
|
246
279
|
verification: {
|
|
247
280
|
command: proposal.verification.command,
|
|
281
|
+
argv: proposal.verification.argv,
|
|
248
282
|
cwd: verifyCwd,
|
|
249
283
|
exitCode: verifyExit,
|
|
250
284
|
stdoutSha256: verifyStdoutSha,
|
|
@@ -268,6 +302,7 @@ function doRun({ flags, cwd }) {
|
|
|
268
302
|
afterSha256: summary.afterSha256,
|
|
269
303
|
verification: {
|
|
270
304
|
command: proposal.verification.command,
|
|
305
|
+
argv: proposal.verification.argv,
|
|
271
306
|
cwd: verifyCwd,
|
|
272
307
|
exitCode: verifyExit,
|
|
273
308
|
stdoutSha256: verifyStdoutSha,
|
|
@@ -286,11 +321,7 @@ function doVerify({ flags, cwd }) {
|
|
|
286
321
|
}
|
|
287
322
|
const proposalPath = isAbsolute(flags.proposal) ? flags.proposal : resolve(cwd, flags.proposal);
|
|
288
323
|
const proposal = validateProposal(JSON.parse(readFileSync(proposalPath, 'utf8')));
|
|
289
|
-
const verify =
|
|
290
|
-
cwd: proposal.verification.cwd || cwd,
|
|
291
|
-
encoding: 'utf8',
|
|
292
|
-
timeout: Number(proposal.verification.timeoutMs) || 60_000,
|
|
293
|
-
});
|
|
324
|
+
const { result: verify } = runVerification(proposal.verification, cwd);
|
|
294
325
|
process.stdout.write(JSON.stringify({
|
|
295
326
|
ok: (verify.status ?? -1) === (proposal.verification.expectedExitCode ?? 0),
|
|
296
327
|
exitCode: verify.status ?? -1,
|
package/cli/commands/install.mjs
CHANGED
|
@@ -24,11 +24,10 @@ export function showInstallHelp() {
|
|
|
24
24
|
Usage:
|
|
25
25
|
bizar install Install (or refresh) every component
|
|
26
26
|
bizar install --dry-run Print what would happen, change nothing
|
|
27
|
-
bizar install --force
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
bizar install --deep Alias for --force (clean-install semantics)
|
|
27
|
+
bizar install --force Repair Bizar-owned files from the ownership ledger.
|
|
28
|
+
bizar install --really-reset-global-claude-config
|
|
29
|
+
Explicit emergency reset of the global Claude config.
|
|
30
|
+
bizar install --deep Alias for --force (ownership-safe repair)
|
|
32
31
|
bizar install --yes Non-interactive install (CI/script friendly)
|
|
33
32
|
bizar install --non-interactive Alias for --yes
|
|
34
33
|
bizar install --help Show this help
|
|
@@ -38,8 +37,9 @@ export function showInstallHelp() {
|
|
|
38
37
|
difference is just mode=install vs mode=update. Every step is
|
|
39
38
|
idempotent — running this twice is safe.
|
|
40
39
|
|
|
41
|
-
|
|
42
|
-
|
|
40
|
+
Ownership-safe repair means --force removes/replaces only unchanged files
|
|
41
|
+
recorded by Bizar. It preserves user and third-party siblings. The explicit
|
|
42
|
+
emergency flag is the only path that wipes:
|
|
43
43
|
- ~/.claude/{agents,skills,commands,hooks,rules,workflows,plugins}/
|
|
44
44
|
- ~/.agents/ (shared skill-registry + lock)
|
|
45
45
|
- ~/.claude/settings.json (env vars backed up to BIZAR_SAVED_ENV)
|
|
@@ -69,7 +69,8 @@ export function showInstallHelp() {
|
|
|
69
69
|
URL and key only when they are not already configured. Fresh setups
|
|
70
70
|
can also choose a default model, agent teams, OpenKan home, and whether
|
|
71
71
|
to initialise the current project's .ok/ workspace.
|
|
72
|
-
9.
|
|
72
|
+
9. Required invariants are checked during provisioning; use 'bizar doctor'
|
|
73
|
+
explicitly for the full diagnostic.
|
|
73
74
|
|
|
74
75
|
Provider settings are global (~/.claude/settings.json), so they work from
|
|
75
76
|
every project. Key input is hidden. Use --yes or --non-interactive to skip
|
|
@@ -86,9 +87,8 @@ export function showUpdateHelp() {
|
|
|
86
87
|
Usage:
|
|
87
88
|
bizar update Refresh every Bizar-managed surface
|
|
88
89
|
bizar update --dry-run Print what would happen, change nothing
|
|
89
|
-
bizar update --force | --deep
|
|
90
|
-
|
|
91
|
-
re-sync everything from the repo
|
|
90
|
+
bizar update --force | --deep Ownership-safe repair of Bizar-managed
|
|
91
|
+
files from the ledger
|
|
92
92
|
bizar update --yes | -y Assume yes for any non-destructive prompt
|
|
93
93
|
bizar update --non-interactive Alias for --yes
|
|
94
94
|
bizar update --help Show this help
|
|
@@ -100,18 +100,15 @@ export function showUpdateHelp() {
|
|
|
100
100
|
1. Ensures the bundled default OpenKan planning runtime is present and current.
|
|
101
101
|
2. Re-emits skills, commands, rules, hooks, agents, and workflows
|
|
102
102
|
from the repo source into ~/.claude/ (or $CLAUDE_CONFIG_DIR),
|
|
103
|
-
overwriting only the Bizar-managed surface and pruning
|
|
104
|
-
entries
|
|
103
|
+
overwriting only the Bizar-managed surface and pruning unchanged
|
|
104
|
+
ledger-owned stale entries.
|
|
105
105
|
3. Writes the install marker so subsequent runs short-circuit when
|
|
106
106
|
nothing has changed.
|
|
107
|
-
4. With --force,
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
URL, auth token, and BIZAR_HOME are preserved across the wipe).
|
|
113
|
-
5. Runs 'bizar doctor' after a successful update so config
|
|
114
|
-
regressions surface before the next Claude Code session.
|
|
107
|
+
4. With --force, repairs only unchanged files recorded in the ownership
|
|
108
|
+
ledger. The emergency global reset option is intentionally documented
|
|
109
|
+
on bizar install, not routine updates.
|
|
110
|
+
5. Runs the narrow installer invariants; use 'bizar doctor' for the full
|
|
111
|
+
diagnostic when needed.
|
|
115
112
|
6. Repairs stale bin symlinks so the operator picks up the new code
|
|
116
113
|
on the next shell prompt.
|
|
117
114
|
|
|
@@ -150,13 +147,19 @@ export function showUpdateHelp() {
|
|
|
150
147
|
async function runPostInstallerRepair({ runRepair: runRepairDep = runRepair } = {}) {
|
|
151
148
|
try {
|
|
152
149
|
const r = await runRepairDep({});
|
|
150
|
+
if (r?.ok === false) {
|
|
151
|
+
console.error(chalk.red(` ✗ Repair failed: ${(r?.notes || []).join(' ') || 'active bizar could not be verified'}`));
|
|
152
|
+
return r;
|
|
153
|
+
}
|
|
153
154
|
if (r.fixed.length > 0) {
|
|
154
155
|
console.log(chalk.cyan('\n Repair: repointed stale bin symlinks:'));
|
|
155
156
|
for (const f of r.fixed) console.log(` ${f}`);
|
|
156
157
|
console.log(chalk.dim(' Re-run your shell or `hash -r` to pick up the new path.'));
|
|
157
158
|
}
|
|
159
|
+
return r;
|
|
158
160
|
} catch (err) {
|
|
159
161
|
console.log(chalk.dim(` Repair skipped: ${err.message}`));
|
|
162
|
+
return { ok: false, error: err.message };
|
|
160
163
|
}
|
|
161
164
|
}
|
|
162
165
|
|
|
@@ -171,8 +174,8 @@ export async function install(args, isHelpRequest) {
|
|
|
171
174
|
// parser for the installer family. Reusing it keeps install and
|
|
172
175
|
// update in lockstep on flag semantics. --deep is parsed as an
|
|
173
176
|
// alias for --force (clean-install semantics, F-183).
|
|
174
|
-
const { mode, dryRun, force, yes } = parseFlags(args);
|
|
175
|
-
const result = await runInstaller({ mode, dryRun, force, yes });
|
|
177
|
+
const { mode, dryRun, force, reallyResetGlobalClaudeConfig, yes } = parseFlags(args);
|
|
178
|
+
const result = await runInstaller({ mode, dryRun, force, reallyResetGlobalClaudeConfig, yes });
|
|
176
179
|
// F-183 — print a one-line summary of the wipe scope so operators
|
|
177
180
|
// can see at a glance what changed without re-reading the verbose
|
|
178
181
|
// step list.
|
|
@@ -186,10 +189,10 @@ export async function install(args, isHelpRequest) {
|
|
|
186
189
|
}
|
|
187
190
|
// v4.4.3 — After install, repair any stale bin symlinks so the
|
|
188
191
|
// user picks up the new code.
|
|
189
|
-
await runPostInstallerRepair();
|
|
192
|
+
const repairResult = await runPostInstallerRepair();
|
|
190
193
|
// v10.19.6 — propagate install failure to the parent shell so
|
|
191
194
|
// `bizar install && bizar doctor` short-circuits on install errors.
|
|
192
|
-
if (!result?.ok) process.exit(1);
|
|
195
|
+
if (!result?.ok || repairResult?.ok === false) process.exit(1);
|
|
193
196
|
}
|
|
194
197
|
|
|
195
198
|
export async function update(args, isHelpRequest) {
|
|
@@ -197,6 +200,16 @@ export async function update(args, isHelpRequest) {
|
|
|
197
200
|
showUpdateHelp();
|
|
198
201
|
return;
|
|
199
202
|
}
|
|
203
|
+
const versionIndex = args.indexOf('--version');
|
|
204
|
+
if (versionIndex >= 0) {
|
|
205
|
+
const version = args[versionIndex + 1];
|
|
206
|
+
const artifactIndex = args.indexOf('--artifact-dir');
|
|
207
|
+
const artifactDir = artifactIndex >= 0 ? args[artifactIndex + 1] : undefined;
|
|
208
|
+
const { runVerifiedUpdate } = await import('../update.mjs');
|
|
209
|
+
const result = await runVerifiedUpdate(args, { artifactDir });
|
|
210
|
+
if (!result?.ok) process.exit(1);
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
200
213
|
// v10.19.6 — route update through the same flag-parsing + installer
|
|
201
214
|
// pipeline as install so `--dry-run`, `--force`, `--yes`, etc. do
|
|
202
215
|
// what they claim. See `runUpdateWithFlags` for the testable
|
|
@@ -232,9 +245,12 @@ export async function runUpdateWithFlags({
|
|
|
232
245
|
parseFlags: parseFlagsDep = parseFlags,
|
|
233
246
|
runRepair: runRepairDep = runRepair,
|
|
234
247
|
} = {}) {
|
|
235
|
-
const { mode, dryRun, force, yes } = parseFlagsDep(args);
|
|
236
|
-
const
|
|
237
|
-
|
|
248
|
+
const { mode, dryRun, force, reallyResetGlobalClaudeConfig, yes } = parseFlagsDep(args);
|
|
249
|
+
const installerOpts = { mode, dryRun, force, yes };
|
|
250
|
+
if (reallyResetGlobalClaudeConfig) installerOpts.reallyResetGlobalClaudeConfig = true;
|
|
251
|
+
const result = await runInstallerDep(installerOpts);
|
|
252
|
+
const repairResult = await runPostInstallerRepair({ runRepair: runRepairDep });
|
|
253
|
+
if (repairResult && repairResult.ok === false) process.exit(1);
|
|
238
254
|
if (!result?.ok) process.exit(1);
|
|
239
255
|
return result;
|
|
240
256
|
}
|