@polderlabs/bizar 10.25.0 → 10.25.2
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 +4 -4
- package/README.md +16 -2
- package/cli/bin.mjs +3 -3
- package/cli/commands/claim.mjs +6 -6
- package/cli/commands/goal-bootstrap.mjs +1 -1
- package/cli/commands/install.mjs +15 -10
- package/cli/commands/openkan.mjs +20 -14
- package/cli/commands/planning.mjs +1 -1
- package/cli/commands/task.mjs +10 -10
- package/cli/doctor.mjs +8 -0
- package/cli/install/index.mjs +9 -2
- package/cli/install/interactive-setup.mjs +100 -6
- package/cli/install/paths.mjs +4 -0
- package/cli/openkan.mjs +323 -123
- package/cli/provision.mjs +114 -22
- package/config/claude/agents/_shared/AGENT_BASELINE.md +1 -1
- package/config/claude/agents/office-manager.md +5 -4
- package/config/claude/commands/bizar.md +2 -2
- package/config/claude/commands/plow-through.md +1 -1
- package/config/claude/commands/team.md +5 -4
- package/config/claude/hooks/goal-bootstrap.mjs +3 -3
- package/config/claude/hooks/sessionend-recall.mjs +1 -1
- package/config/claude/hooks/sessionstart-prime.mjs +3 -3
- package/config/claude/hooks/verify-deliverables.mjs +1 -1
- package/config/claude/hooks/worktree-bootstrap.mjs +1 -1
- package/config/skills/autopilot/SKILL.md +1 -1
- package/config/skills/goal-bootstrap/SKILL.md +5 -5
- package/config/skills/harness-engineering/SKILL.md +1 -1
- package/config/skills/openkan/SKILL.md +9 -9
- package/config/skills/ultragoal/SKILL.md +3 -3
- package/config/skills/ultrawork/SKILL.md +1 -1
- package/install.sh +14 -4
- package/package.json +2 -2
- package/packages/sdk/dist/mcp/server.js +62 -7
- 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/config/claude/CLAUDE.md +0 -213
- package/config/skills/bizar/README.md +0 -9
- package/config/skills/bizar/SKILL.md +0 -48
package/AGENTS.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Bizar Harness is a Claude Code-native, guarded-autonomy harness. It ships project and user-level agents, skills, slash commands, hooks, an MCP server, CLI utilities, and verification scripts. OpenKan is bundled as Bizar’s default durable planning, progression, task, and PRD-goal system; Bizar integrates with it only through the `.ok/` workspace and its supported CLI boundary.
|
|
4
4
|
|
|
5
|
-
If you are an agent: read this file, inspect `.ok/` with `
|
|
5
|
+
If you are an agent: read this file, inspect `.ok/` with `ok task list` and `ok prd list`, then run `make check` before changing code.
|
|
6
6
|
|
|
7
7
|
## Commands
|
|
8
8
|
|
|
@@ -15,9 +15,9 @@ make check-arch # architectural and removed-surface checks
|
|
|
15
15
|
make verify-removed-surfaces # prove deleted subsystems are absent
|
|
16
16
|
make verify-repo-structure # prove tracked/package paths are clean
|
|
17
17
|
make clean-check # debug-artifact/static hygiene gate
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
18
|
+
ok task list # OpenKan task progression
|
|
19
|
+
ok plan list # OpenKan plans
|
|
20
|
+
ok prd list # OpenKan PRD goals
|
|
21
21
|
make session-start # lifecycle compatibility target
|
|
22
22
|
make session-end # lifecycle compatibility target
|
|
23
23
|
```
|
package/README.md
CHANGED
|
@@ -54,9 +54,18 @@ bizar models
|
|
|
54
54
|
|
|
55
55
|
Restart Claude Code after installation. The installer adds Bizar's agents, skills, commands, hooks, settings, and the
|
|
56
56
|
default OpenKan planning runtime to your user-level Claude configuration.
|
|
57
|
+
Claude Code itself is installed with Anthropic's native installer; npm is used
|
|
58
|
+
for Bizar and OpenKan, not for the Claude Code CLI.
|
|
57
59
|
It preserves your configured gateway endpoint and credential values during a
|
|
58
60
|
clean reinstall.
|
|
59
61
|
|
|
62
|
+
On a new interactive install, Bizar also asks for an optional default model,
|
|
63
|
+
whether Claude Code agent teams should be enabled, the OpenKan install
|
|
64
|
+
directory, and whether the current project should receive a `.ok/` workspace.
|
|
65
|
+
OpenKan is installed from npm as `@polderlabs/openkan@latest`; its package-owned
|
|
66
|
+
agent and skill are installed into the same Claude configuration. Use
|
|
67
|
+
`bizar install --yes` for CI or a prompt-free refresh.
|
|
68
|
+
|
|
60
69
|
For a completely fresh Bizar-managed Claude setup while retaining endpoint and
|
|
61
70
|
authentication settings:
|
|
62
71
|
|
|
@@ -190,6 +199,8 @@ point where an external or difficult-to-reverse decision belongs to you.
|
|
|
190
199
|
|
|
191
200
|
~/.config/bizar/
|
|
192
201
|
├── installed.json install record
|
|
202
|
+
├── openkan/ managed @polderlabs/openkan npm runtime
|
|
203
|
+
├── openkan-install.json selected OpenKan home/package settings
|
|
193
204
|
├── evidence/ local dispatch and verification evidence
|
|
194
205
|
├── telemetry/ local routing and rejected-action feedback
|
|
195
206
|
└── worktree-queue.json completed worktree integration queue
|
|
@@ -203,11 +214,14 @@ plane, background daemon, or general-purpose note vault.
|
|
|
203
214
|
|
|
204
215
|
| Command | When to use it |
|
|
205
216
|
| --- | --- |
|
|
206
|
-
| `bizar install` | Install or refresh Bizar
|
|
217
|
+
| `bizar install` | Install or refresh Bizar, OpenKan, and the global Claude configuration |
|
|
218
|
+
| `bizar openkan install` | Refresh the managed `@polderlabs/openkan` npm runtime and its agent/skill |
|
|
219
|
+
| `bizar openkan init` | Initialise `.ok/` planning state in the current project |
|
|
207
220
|
| `bizar models` | Discover and select the models Bizar may dispatch |
|
|
208
221
|
| `bizar doctor` | Diagnose the global installation and provider connectivity |
|
|
209
222
|
| `bizar validate` | Run an install-focused health check |
|
|
210
|
-
| `
|
|
223
|
+
| `ok task` | Inspect or coordinate scoped worktree tasks (OpenKan-native) |
|
|
224
|
+
| `ok plan` / `ok prd` | Manage OpenKan plans and PRD goals |
|
|
211
225
|
| `bizar worktree-merge --all` | Review and merge completed isolated work, surfacing conflicts |
|
|
212
226
|
| `bizar control snapshot --json` | Read the machine-friendly current harness state |
|
|
213
227
|
| `bizar evidence` | Inspect local dispatch and verification evidence |
|
package/cli/bin.mjs
CHANGED
|
@@ -154,7 +154,7 @@ function showHelp() {
|
|
|
154
154
|
|
|
155
155
|
Install:
|
|
156
156
|
npm install -g @polderlabs/bizar
|
|
157
|
-
|
|
157
|
+
curl -fsSL https://claude.ai/install.sh | bash
|
|
158
158
|
`);
|
|
159
159
|
}
|
|
160
160
|
|
|
@@ -399,7 +399,7 @@ async function main() {
|
|
|
399
399
|
dbg('loaded command module:', 'claim');
|
|
400
400
|
const found = await mod.run(cmd, cmdArgs, isHelpRequest);
|
|
401
401
|
if (found === false) {
|
|
402
|
-
console.error(chalk.red(` ✗ Usage:
|
|
402
|
+
console.error(chalk.red(` ✗ Usage: ok task claim <task-id> — run 'ok task --help'`));
|
|
403
403
|
process.exit(EXIT_USAGE);
|
|
404
404
|
}
|
|
405
405
|
break;
|
|
@@ -415,7 +415,7 @@ async function main() {
|
|
|
415
415
|
dbg('loaded command module:', 'task');
|
|
416
416
|
const found = await mod.run(cmd, cmdArgs, isHelpRequest);
|
|
417
417
|
if (found === false) {
|
|
418
|
-
console.error(chalk.red(` ✗ Usage:
|
|
418
|
+
console.error(chalk.red(` ✗ Usage: ok task <subcommand> — run 'ok task --help'`));
|
|
419
419
|
process.exit(EXIT_USAGE);
|
|
420
420
|
}
|
|
421
421
|
break;
|
package/cli/commands/claim.mjs
CHANGED
|
@@ -3,16 +3,16 @@ import { OpenKanError, runOpenKanOk } from '../openkan.mjs';
|
|
|
3
3
|
|
|
4
4
|
function help() {
|
|
5
5
|
process.stdout.write(`
|
|
6
|
-
|
|
6
|
+
ok task claim — claim an OpenKan task lease
|
|
7
7
|
|
|
8
8
|
Usage:
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
9
|
+
ok task claim <task-id> [--who <agent>] [--lease-ms <ms>]
|
|
10
|
+
ok task release <task-id> [--who <agent>]
|
|
11
|
+
ok task heartbeat <task-id> [--who <agent>] [--lease-ms <ms>]
|
|
12
|
+
ok task list [--status <status>] [--json]
|
|
13
13
|
|
|
14
14
|
Claims are OpenKan .ok locks, not feature_list.json annotations. Use
|
|
15
|
-
\`
|
|
15
|
+
\`ok task update\` for status transitions and \`ok prd\` for PRDs.
|
|
16
16
|
`);
|
|
17
17
|
}
|
|
18
18
|
|
|
@@ -9,7 +9,7 @@ function usage() {
|
|
|
9
9
|
bizar goal-bootstrap — retired compatibility alias
|
|
10
10
|
|
|
11
11
|
OpenKan PRDs are Bizar's default durable goal system.
|
|
12
|
-
Use:
|
|
12
|
+
Use: ok prd list | ok prd add <title> | ok prd update <id> ...
|
|
13
13
|
`);
|
|
14
14
|
}
|
|
15
15
|
|
package/cli/commands/install.mjs
CHANGED
|
@@ -53,18 +53,23 @@ export function showInstallHelp() {
|
|
|
53
53
|
while the prior ANTHROPIC_* and BIZAR_* env vars are merged back
|
|
54
54
|
in from the stash.
|
|
55
55
|
|
|
56
|
-
1.
|
|
57
|
-
2. Installs or updates
|
|
58
|
-
|
|
59
|
-
3.
|
|
60
|
-
|
|
56
|
+
1. Uses the already-installed @polderlabs/bizar package.
|
|
57
|
+
2. Installs or updates Claude Code with Anthropic's native installer
|
|
58
|
+
(never npm) when the native launcher is absent.
|
|
59
|
+
3. Installs or updates OpenKan from npm as Bizar's default durable
|
|
60
|
+
planning, progression, and PRD-goal runtime.
|
|
61
|
+
4. Uses platform dependencies prepared by ./install.sh when invoked
|
|
62
|
+
through that bootstrap.
|
|
63
|
+
5. Syncs agent files, slash commands, and bundled skills into
|
|
61
64
|
~/.claude/ (or $CLAUDE_CONFIG_DIR).
|
|
62
|
-
|
|
63
|
-
|
|
65
|
+
6. Registers the Bizar MCP server in ~/.claude/settings.json.
|
|
66
|
+
7. Wires Claude Code lifecycle hooks (SessionStart / PreToolUse /
|
|
64
67
|
PostToolUse / UserPromptSubmit) under ~/.claude/hooks/.
|
|
65
|
-
|
|
66
|
-
URL and key only when they are not already configured.
|
|
67
|
-
|
|
68
|
+
8. In a terminal, confirms the install and securely asks for a provider
|
|
69
|
+
URL and key only when they are not already configured. Fresh setups
|
|
70
|
+
can also choose a default model, agent teams, OpenKan home, and whether
|
|
71
|
+
to initialise the current project's .ok/ workspace.
|
|
72
|
+
9. Runs 'bizar doctor' as a post-install health check.
|
|
68
73
|
|
|
69
74
|
Provider settings are global (~/.claude/settings.json), so they work from
|
|
70
75
|
every project. Key input is hidden. Use --yes or --non-interactive to skip
|
package/cli/commands/openkan.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { spawnSync } from 'node:child_process';
|
|
2
|
-
import {
|
|
2
|
+
import { ensureOpenKanProject, installOpenKanPromise, OpenKanError, resolveOpenKanDashboard, runOpenKanOk } from '../openkan.mjs';
|
|
3
3
|
|
|
4
4
|
function print(result) {
|
|
5
5
|
if (result.stdout) process.stdout.write(result.stdout);
|
|
@@ -12,24 +12,29 @@ function help() {
|
|
|
12
12
|
bizar openkan — default durable planning and goals surface
|
|
13
13
|
|
|
14
14
|
Usage:
|
|
15
|
-
bizar openkan install Install
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
15
|
+
bizar openkan install Install @polderlabs/openkan@latest from npm
|
|
16
|
+
ok init Initialise .ok/ in this project
|
|
17
|
+
ok task <add|list|show|update|...> Manage durable tasks
|
|
18
|
+
ok plan <add|list|show|update> Manage plans and phases
|
|
19
|
+
ok prd <add|list|show|update> Manage PRDs, goals, and milestones
|
|
20
|
+
ok doctor Validate the .ok/ workspace
|
|
21
21
|
bizar openkan dashboard [args...] Forward to the OpenKan dashboard CLI
|
|
22
22
|
|
|
23
|
-
|
|
24
|
-
same OpenKan workspace. Bizar
|
|
23
|
+
Canonical commands are \`ok task\`, \`ok plan\`, and \`ok prd\`; they use this
|
|
24
|
+
same OpenKan workspace. The Bizar planning aliases remain compatibility-only.
|
|
25
|
+
Bizar no longer creates a SQLite task ledger or
|
|
25
26
|
feature/progress files for live planning.
|
|
26
27
|
`);
|
|
27
28
|
}
|
|
28
29
|
|
|
29
30
|
function runDashboard(args) {
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
|
|
31
|
+
const launcher = resolveOpenKanDashboard();
|
|
32
|
+
const command = launcher.endsWith('.ts')
|
|
33
|
+
? [process.execPath, '--experimental-strip-types', launcher, ...args]
|
|
34
|
+
: launcher.endsWith('.mjs')
|
|
35
|
+
? [process.execPath, launcher, ...args]
|
|
36
|
+
: [launcher, ...args];
|
|
37
|
+
const result = spawnSync(command[0], command.slice(1), { cwd: process.cwd(), encoding: 'utf8', shell: false });
|
|
33
38
|
if (result.error) throw result.error;
|
|
34
39
|
print({ ok: result.status === 0, status: result.status ?? 1, stdout: result.stdout || '', stderr: result.stderr || '' });
|
|
35
40
|
}
|
|
@@ -40,8 +45,9 @@ export async function run(name, args, isHelpRequest) {
|
|
|
40
45
|
if (isHelpRequest || !subcommand || subcommand === 'help') { help(); return true; }
|
|
41
46
|
try {
|
|
42
47
|
if (subcommand === 'install') {
|
|
43
|
-
const result = await installOpenKanPromise();
|
|
44
|
-
|
|
48
|
+
const result = await installOpenKanPromise({ persistConfig: true });
|
|
49
|
+
process.stdout.write(` ✓ ${result.message}\n`);
|
|
50
|
+
if (result.agent?.skipped) process.stdout.write(' ! OpenKan agent/skill installation was skipped by configuration.\n');
|
|
45
51
|
return true;
|
|
46
52
|
}
|
|
47
53
|
if (subcommand === 'init') {
|
|
@@ -3,7 +3,7 @@ import { OpenKanError, runOpenKanOk } from '../openkan.mjs';
|
|
|
3
3
|
|
|
4
4
|
function usage(name) {
|
|
5
5
|
const resource = name === 'goals' ? 'prd' : 'plan';
|
|
6
|
-
process.stdout.write(`\n bizar ${name} — OpenKan ${resource}
|
|
6
|
+
process.stdout.write(`\n bizar ${name} — compatibility alias for OpenKan ${resource}\n\n Usage: ok ${resource} <args...>\n Run \`ok ${resource} --help\` for the canonical OpenKan surface.\n`);
|
|
7
7
|
}
|
|
8
8
|
|
|
9
9
|
export async function run(name, args, isHelpRequest) {
|
package/cli/commands/task.mjs
CHANGED
|
@@ -3,18 +3,18 @@ import { OpenKanError, runOpenKanOk } from '../openkan.mjs';
|
|
|
3
3
|
|
|
4
4
|
function help() {
|
|
5
5
|
process.stdout.write(`
|
|
6
|
-
|
|
6
|
+
ok task — OpenKan-backed task lifecycle
|
|
7
7
|
|
|
8
8
|
Usage:
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
9
|
+
ok task add <title> [--owner agent] [--priority p0|p1|p2|p3]
|
|
10
|
+
ok task list [--status pending|in_progress|review|done|cancelled] [--json]
|
|
11
|
+
ok task show <id> [--json]
|
|
12
|
+
ok task claim <id> --owner <agent> [--lease-ms <ms>]
|
|
13
|
+
ok task heartbeat <id> --owner <agent> [--lease-ms <ms>]
|
|
14
|
+
ok task update <id> --status <status> [--evidence <text>]
|
|
15
|
+
ok task complete <id> --owner <agent> --evidence <text>
|
|
16
|
+
ok task cancel <id> --owner <agent> --reason <text>
|
|
17
|
+
ok task release <id> --owner <agent>
|
|
18
18
|
|
|
19
19
|
This is an exact convenience alias for \`ok task\`. OpenKan persists task
|
|
20
20
|
state, scoped ownership, dependencies, evidence, plans, and goals under
|
package/cli/doctor.mjs
CHANGED
|
@@ -43,6 +43,7 @@ import {
|
|
|
43
43
|
} from './commands/validate.mjs';
|
|
44
44
|
import { configuredEnabledModels, listModels, resolveEndpoint } from './commands/models.mjs';
|
|
45
45
|
import { validateNativeWorkflowDirectory } from '../config/workflows/lib/native-contract.mjs';
|
|
46
|
+
import { resolveOpenKanHome, verifyOpenKanRuntime } from './openkan.mjs';
|
|
46
47
|
|
|
47
48
|
const REQUIRED_RULES = [
|
|
48
49
|
'general.md', 'git.md', 'javascript.md', 'python.md',
|
|
@@ -188,6 +189,12 @@ async function checkBizarHome() {
|
|
|
188
189
|
return `BIZAR_HOME present at ${dir}`;
|
|
189
190
|
}
|
|
190
191
|
|
|
192
|
+
async function checkOpenKanRuntime() {
|
|
193
|
+
const home = resolveOpenKanHome();
|
|
194
|
+
const result = verifyOpenKanRuntime();
|
|
195
|
+
return `OpenKan ${home} is runnable (${result.launcher})`;
|
|
196
|
+
}
|
|
197
|
+
|
|
191
198
|
async function checkProviderReachable() {
|
|
192
199
|
const { endpoint, authToken } = resolveEndpoint();
|
|
193
200
|
if (!endpoint) {
|
|
@@ -227,6 +234,7 @@ const CHECKS = [
|
|
|
227
234
|
{ name: 'native-workflows-valid', run: checkNativeWorkflowsInstalled },
|
|
228
235
|
{ name: 'tools-on-path', run: checkToolsAvailable },
|
|
229
236
|
{ name: 'bizar-home', run: checkBizarHome },
|
|
237
|
+
{ name: 'openkan-runtime', run: checkOpenKanRuntime },
|
|
230
238
|
{ name: 'provider-reachable', run: checkProviderReachable },
|
|
231
239
|
];
|
|
232
240
|
|
package/cli/install/index.mjs
CHANGED
|
@@ -48,7 +48,7 @@ export async function runInstaller(opts = {}) {
|
|
|
48
48
|
// via --yes / --non-interactive, and update runs never request credentials.
|
|
49
49
|
let interactive = null;
|
|
50
50
|
if (mode !== 'update' && !dryRun) {
|
|
51
|
-
interactive = await runInteractiveSetup({ enabled: !yes });
|
|
51
|
+
interactive = await runInteractiveSetup({ enabled: !yes, cwd: process.cwd() });
|
|
52
52
|
if (!interactive.ok) return { ok: false, interactive };
|
|
53
53
|
if (interactive.cancelled) return { ok: true, cancelled: true, interactive };
|
|
54
54
|
}
|
|
@@ -78,7 +78,14 @@ export async function runInstaller(opts = {}) {
|
|
|
78
78
|
// Always pass `force: true` downstream so `runProvision` re-emits the
|
|
79
79
|
// template-owned keys (permissions.allow wildcards, mcpServers, hooks)
|
|
80
80
|
// into the freshly-empty settings file.
|
|
81
|
-
const provisionResult = await runProvision({
|
|
81
|
+
const provisionResult = await runProvision({
|
|
82
|
+
mode,
|
|
83
|
+
dryRun,
|
|
84
|
+
force: true,
|
|
85
|
+
yes,
|
|
86
|
+
openkanHome: interactive?.openkanHome,
|
|
87
|
+
initializeOpenKanProject: interactive?.initializeOpenKanProject === true,
|
|
88
|
+
});
|
|
82
89
|
|
|
83
90
|
// F-183 — post-install health check. Surfaced as a warning rather
|
|
84
91
|
// than a hard failure so a forced install that completes without
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { existsSync, readFileSync } from 'node:fs';
|
|
2
2
|
import { homedir } from 'node:os';
|
|
3
|
-
import { join } from 'node:path';
|
|
3
|
+
import { join, resolve } from 'node:path';
|
|
4
4
|
import { Writable } from 'node:stream';
|
|
5
5
|
import { createInterface } from 'node:readline/promises';
|
|
6
|
+
import { resolveOpenKanHome } from '../openkan.mjs';
|
|
6
7
|
|
|
7
8
|
export function providerSettingsPath(env = process.env) {
|
|
8
9
|
const root = env.CLAUDE_CONFIG_DIR?.trim()
|
|
@@ -30,6 +31,16 @@ export function detectProviderConfiguration({ env = process.env, settings = {} }
|
|
|
30
31
|
return { url, key, missing: [...(!url ? ['url'] : []), ...(!key ? ['key'] : [])] };
|
|
31
32
|
}
|
|
32
33
|
|
|
34
|
+
export function detectAdvancedConfiguration({ env = process.env, settings = {} } = {}) {
|
|
35
|
+
const settingsEnv = settings?.env && typeof settings.env === 'object' ? settings.env : {};
|
|
36
|
+
return {
|
|
37
|
+
model: env.ANTHROPIC_MODEL?.trim() || settingsEnv.ANTHROPIC_MODEL?.trim() || '',
|
|
38
|
+
teams: env.CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS?.trim()
|
|
39
|
+
|| settingsEnv.CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS?.trim()
|
|
40
|
+
|| '',
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
33
44
|
export function isValidProviderUrl(value) {
|
|
34
45
|
try {
|
|
35
46
|
const parsed = new URL(value);
|
|
@@ -67,6 +78,30 @@ function writeLine(output, value = '') {
|
|
|
67
78
|
output.write(`${value}\n`);
|
|
68
79
|
}
|
|
69
80
|
|
|
81
|
+
function answer(value) {
|
|
82
|
+
return typeof value === 'string' ? value.trim() : '';
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
async function askYesNo(prompt, defaultValue, deps) {
|
|
86
|
+
while (true) {
|
|
87
|
+
const suffix = defaultValue ? '[Y/n]' : '[y/N]';
|
|
88
|
+
const value = answer(await deps.askText(`${prompt} ${suffix} `, deps.io));
|
|
89
|
+
if (!value) return defaultValue;
|
|
90
|
+
if (['y', 'yes'].includes(value.toLowerCase())) return true;
|
|
91
|
+
if (['n', 'no'].includes(value.toLowerCase())) return false;
|
|
92
|
+
writeLine(deps.output, ' ! Please answer yes or no.');
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function expandHome(value, env) {
|
|
97
|
+
const home = env.HOME?.trim() || homedir();
|
|
98
|
+
return value === '~' ? home : value.startsWith('~/') ? join(home, value.slice(2)) : value;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export function isValidOpenKanHome(value) {
|
|
102
|
+
return Boolean(value && !value.includes('\0'));
|
|
103
|
+
}
|
|
104
|
+
|
|
70
105
|
/**
|
|
71
106
|
* Guided install preflight. Credentials are placed only in the current
|
|
72
107
|
* process; the provisioner's settings writer persists them globally with the
|
|
@@ -77,6 +112,7 @@ export async function runInteractiveSetup({
|
|
|
77
112
|
input = process.stdin,
|
|
78
113
|
output = process.stdout,
|
|
79
114
|
enabled = true,
|
|
115
|
+
cwd = process.cwd(),
|
|
80
116
|
readSettings = readProviderSettings,
|
|
81
117
|
askText = askLine,
|
|
82
118
|
askHidden = askSecret,
|
|
@@ -89,6 +125,7 @@ export async function runInteractiveSetup({
|
|
|
89
125
|
}
|
|
90
126
|
|
|
91
127
|
const detected = detectProviderConfiguration({ env, settings });
|
|
128
|
+
const advanced = detectAdvancedConfiguration({ env, settings });
|
|
92
129
|
const interactive = enabled && input.isTTY === true && output.isTTY === true;
|
|
93
130
|
if (!interactive) {
|
|
94
131
|
if (detected.missing.length > 0) {
|
|
@@ -111,7 +148,7 @@ export async function runInteractiveSetup({
|
|
|
111
148
|
|
|
112
149
|
writeLine(output, '');
|
|
113
150
|
writeLine(output, ' Interactive setup');
|
|
114
|
-
const confirmation = (await askText(' Continue with the installation? [Y/n] ', { input, output })).
|
|
151
|
+
const confirmation = answer(await askText(' Continue with the installation? [Y/n] ', { input, output })).toLowerCase();
|
|
115
152
|
if (confirmation === 'n' || confirmation === 'no') {
|
|
116
153
|
writeLine(output, ' Installation cancelled.');
|
|
117
154
|
return { ok: true, interactive: true, cancelled: true, configured: detected.missing.length === 0 };
|
|
@@ -121,12 +158,12 @@ export async function runInteractiveSetup({
|
|
|
121
158
|
let key = detected.key;
|
|
122
159
|
if (url) writeLine(output, ` ✓ Provider URL detected: ${url}`);
|
|
123
160
|
while (!url) {
|
|
124
|
-
const
|
|
125
|
-
if (!isValidProviderUrl(
|
|
161
|
+
const value = answer(await askText(' Provider URL (for example https://gateway.example/v1): ', { input, output }));
|
|
162
|
+
if (!isValidProviderUrl(value)) {
|
|
126
163
|
writeLine(output, ' ! Enter a valid http:// or https:// URL.');
|
|
127
164
|
continue;
|
|
128
165
|
}
|
|
129
|
-
url =
|
|
166
|
+
url = value.replace(/\/+$/, '');
|
|
130
167
|
}
|
|
131
168
|
|
|
132
169
|
if (key) writeLine(output, ' ✓ Provider key detected (hidden)');
|
|
@@ -139,5 +176,62 @@ export async function runInteractiveSetup({
|
|
|
139
176
|
env.BIZAR_MODEL_ROUTER_URL = url;
|
|
140
177
|
env.ANTHROPIC_AUTH_TOKEN = key;
|
|
141
178
|
writeLine(output, ' ✓ Provider configuration ready; the key will be stored in global Claude settings.');
|
|
142
|
-
|
|
179
|
+
|
|
180
|
+
// Keep the common already-configured path quick. Fresh provider setup gets
|
|
181
|
+
// the extra choices that materially affect the first Bizar session.
|
|
182
|
+
let model = advanced.model;
|
|
183
|
+
let teams = advanced.teams
|
|
184
|
+
? advanced.teams === '1' || advanced.teams.toLowerCase() === 'true'
|
|
185
|
+
: null;
|
|
186
|
+
let openkanHome = resolveOpenKanHome({ cwd, env });
|
|
187
|
+
let initializeOpenKanProject = false;
|
|
188
|
+
if (detected.missing.length > 0) {
|
|
189
|
+
if (model) writeLine(output, ` ✓ Default model detected: ${model}`);
|
|
190
|
+
else {
|
|
191
|
+
model = answer(await askText(' Default Claude model (optional; press Enter to choose later): ', { input, output }));
|
|
192
|
+
if (model) writeLine(output, ` ✓ Default model: ${model}`);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
const teamsEnabled = await askYesNo(' Enable Claude Code agent teams?', true, {
|
|
196
|
+
askText: async (prompt, io) => askText(prompt, io), io: { input, output }, output,
|
|
197
|
+
});
|
|
198
|
+
teams = teamsEnabled;
|
|
199
|
+
writeLine(output, ` ✓ Agent teams ${teams ? 'enabled' : 'disabled'}.`);
|
|
200
|
+
|
|
201
|
+
const homeAnswer = answer(await askText(` OpenKan install directory [${openkanHome}]: `, { input, output }));
|
|
202
|
+
if (homeAnswer) {
|
|
203
|
+
const expanded = expandHome(homeAnswer, env);
|
|
204
|
+
if (!isValidOpenKanHome(expanded)) {
|
|
205
|
+
writeLine(output, ' ! OpenKan directory cannot be empty or contain NUL bytes; using the default.');
|
|
206
|
+
} else {
|
|
207
|
+
openkanHome = resolve(cwd, expanded);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
writeLine(output, ` ✓ OpenKan will be installed at ${openkanHome}.`);
|
|
211
|
+
|
|
212
|
+
const projectHasOpenKan = existsSync(join(cwd, '.ok', 'index.json'));
|
|
213
|
+
if (projectHasOpenKan) {
|
|
214
|
+
writeLine(output, ' ✓ OpenKan project workspace already exists.');
|
|
215
|
+
} else {
|
|
216
|
+
initializeOpenKanProject = await askYesNo(' Initialise OpenKan planning in this project?', true, {
|
|
217
|
+
askText: async (prompt, io) => askText(prompt, io), io: { input, output }, output,
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
if (detected.missing.length > 0) {
|
|
222
|
+
if (model) env.ANTHROPIC_MODEL = model;
|
|
223
|
+
env.CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS = teams ? '1' : '0';
|
|
224
|
+
}
|
|
225
|
+
env.BIZAR_OPENKAN_HOME = openkanHome;
|
|
226
|
+
return {
|
|
227
|
+
ok: true,
|
|
228
|
+
interactive: true,
|
|
229
|
+
cancelled: false,
|
|
230
|
+
configured: true,
|
|
231
|
+
missing: detected.missing,
|
|
232
|
+
model: model || null,
|
|
233
|
+
agentTeams: teams,
|
|
234
|
+
openkanHome,
|
|
235
|
+
initializeOpenKanProject,
|
|
236
|
+
};
|
|
143
237
|
}
|
package/cli/install/paths.mjs
CHANGED
|
@@ -9,6 +9,7 @@ import chalk from 'chalk';
|
|
|
9
9
|
import { homedir } from 'node:os';
|
|
10
10
|
import { join } from 'node:path';
|
|
11
11
|
import { resolveClaudeConfigDir } from '../config-paths.mjs';
|
|
12
|
+
import { resolveOpenKanHome } from '../openkan.mjs';
|
|
12
13
|
|
|
13
14
|
/** Resolve the Claude Code config directory.
|
|
14
15
|
* 1. `process.env.CLAUDE_CONFIG_DIR`
|
|
@@ -35,6 +36,7 @@ export const PATHS = {
|
|
|
35
36
|
hooksDir: join(CLAUDE_DIR, 'hooks'),
|
|
36
37
|
settingsFile: join(CLAUDE_DIR, 'settings.json'),
|
|
37
38
|
bizarHome: BIZAR_HOME,
|
|
39
|
+
openkanHome: resolveOpenKanHome(),
|
|
38
40
|
loopsDir: join(BIZAR_HOME, 'loops'),
|
|
39
41
|
installMarker: join(BIZAR_HOME, 'installed.json'),
|
|
40
42
|
};
|
|
@@ -60,6 +62,8 @@ export function printInstallLocations(_opts = {}) {
|
|
|
60
62
|
console.log(chalk.bold('│') + ` Bizar runtime state ${PATHS.bizarHome}`.padEnd(69) + chalk.bold('│'));
|
|
61
63
|
console.log(chalk.bold('│') + ` ├─ loops/ autonomous loop state`.padEnd(69) + chalk.bold('│'));
|
|
62
64
|
console.log(chalk.bold('│') + ` └─ installed.json install manifest (version, hash)`.padEnd(69) + chalk.bold('│'));
|
|
65
|
+
console.log(chalk.bold('│') + ` OpenKan runtime ${PATHS.openkanHome}`.padEnd(69) + chalk.bold('│'));
|
|
66
|
+
console.log(chalk.bold('│') + ` └─ npm package @polderlabs/openkan@latest + agent/skill`.padEnd(69) + chalk.bold('│'));
|
|
63
67
|
console.log(chalk.bold('│') + ' '.repeat(68) + chalk.bold('│'));
|
|
64
68
|
console.log(chalk.bold('│') + ` Override with: ${dim('CLAUDE_CONFIG_DIR=/path/to/dir')}`.padEnd(69) + chalk.bold('│'));
|
|
65
69
|
console.log(chalk.bold('└──────────────────────────────────────────────────────────────────────┘'));
|