@polderlabs/bizar 10.25.1 → 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.
Files changed (40) hide show
  1. package/AGENTS.md +4 -4
  2. package/README.md +16 -2
  3. package/cli/bin.mjs +3 -3
  4. package/cli/commands/claim.mjs +6 -6
  5. package/cli/commands/goal-bootstrap.mjs +1 -1
  6. package/cli/commands/install.mjs +15 -10
  7. package/cli/commands/openkan.mjs +19 -13
  8. package/cli/commands/planning.mjs +1 -1
  9. package/cli/commands/task.mjs +10 -10
  10. package/cli/doctor.mjs +8 -0
  11. package/cli/install/index.mjs +9 -2
  12. package/cli/install/interactive-setup.mjs +100 -6
  13. package/cli/install/paths.mjs +4 -0
  14. package/cli/openkan.mjs +212 -10
  15. package/cli/provision.mjs +114 -22
  16. package/config/claude/agents/_shared/AGENT_BASELINE.md +1 -1
  17. package/config/claude/agents/office-manager.md +5 -4
  18. package/config/claude/commands/bizar.md +2 -2
  19. package/config/claude/commands/plow-through.md +1 -1
  20. package/config/claude/commands/team.md +5 -4
  21. package/config/claude/hooks/goal-bootstrap.mjs +3 -3
  22. package/config/claude/hooks/sessionend-recall.mjs +1 -1
  23. package/config/claude/hooks/sessionstart-prime.mjs +3 -3
  24. package/config/claude/hooks/verify-deliverables.mjs +1 -1
  25. package/config/claude/hooks/worktree-bootstrap.mjs +1 -1
  26. package/config/skills/autopilot/SKILL.md +1 -1
  27. package/config/skills/goal-bootstrap/SKILL.md +5 -5
  28. package/config/skills/harness-engineering/SKILL.md +1 -1
  29. package/config/skills/openkan/SKILL.md +9 -9
  30. package/config/skills/ultragoal/SKILL.md +3 -3
  31. package/config/skills/ultrawork/SKILL.md +1 -1
  32. package/install.sh +14 -4
  33. package/package.json +2 -2
  34. package/packages/sdk/dist/mcp/server.js +62 -7
  35. package/packages/sdk/dist/version.d.ts +1 -1
  36. package/packages/sdk/dist/version.js +1 -1
  37. package/packages/sdk/package.json +1 -1
  38. package/config/claude/CLAUDE.md +0 -213
  39. package/config/skills/bizar/README.md +0 -9
  40. 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 `bizar task list` and `bizar goals list`, then run `make check` before changing code.
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
- bizar task list # OpenKan task progression
19
- bizar plan list # OpenKan plans
20
- bizar goals list # OpenKan PRD goals
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 in your global Claude configuration |
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
- | `bizar task` | Inspect or coordinate scoped worktree tasks |
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
- npm install -g @anthropic-ai/claude-code
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: bizar claim <subcommand> — run 'bizar claim --help'`));
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: bizar task <subcommand> — run 'bizar task --help'`));
418
+ console.error(chalk.red(` ✗ Usage: ok task <subcommand> — run 'ok task --help'`));
419
419
  process.exit(EXIT_USAGE);
420
420
  }
421
421
  break;
@@ -3,16 +3,16 @@ import { OpenKanError, runOpenKanOk } from '../openkan.mjs';
3
3
 
4
4
  function help() {
5
5
  process.stdout.write(`
6
- bizar claim — claim an OpenKan task lease
6
+ ok task claim — claim an OpenKan task lease
7
7
 
8
8
  Usage:
9
- bizar claim <task-id> [--who <agent>] [--lease-ms <ms>]
10
- bizar claim release <task-id> [--who <agent>]
11
- bizar claim heartbeat <task-id> [--who <agent>] [--lease-ms <ms>]
12
- bizar claim list [--status <status>] [--json]
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
- \`bizar task update\` for status transitions and \`bizar goals\` for PRDs.
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: bizar goals list | bizar goals add <title> | bizar goals update <id> ...
12
+ Use: ok prd list | ok prd add <title> | ok prd update <id> ...
13
13
  `);
14
14
  }
15
15
 
@@ -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. Installs @polderlabs/bizar via npm (skipped if already current).
57
- 2. Installs or updates OpenKan from its official installer as Bizar's
58
- default durable planning, progression, and PRD-goal runtime.
59
- 3. Shells to ./install.sh for platform-specific system dependencies.
60
- 4. Syncs agent files, slash commands, and bundled skills into
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
- 5. Registers the Bizar MCP server in ~/.claude/settings.json.
63
- 6. Wires Claude Code lifecycle hooks (SessionStart / PreToolUse /
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
- 7. In a terminal, confirms the install and securely asks for a provider
66
- URL and key only when they are not already configured.
67
- 8. Runs 'bizar doctor' as a post-install health check.
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
@@ -1,5 +1,5 @@
1
1
  import { spawnSync } from 'node:child_process';
2
- import { executableOnPath, ensureOpenKanProject, installOpenKanPromise, OpenKanError, runOpenKanOk } from '../openkan.mjs';
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);
@@ -13,23 +13,28 @@ bizar openkan — default durable planning and goals surface
13
13
 
14
14
  Usage:
15
15
  bizar openkan install Install @polderlabs/openkan@latest from npm
16
- bizar openkan init Initialise .ok/ in this project
17
- bizar openkan task <ok task args...> Manage durable tasks
18
- bizar openkan plan <ok plan args...> Manage plans and phases
19
- bizar openkan goals <ok prd args...> Manage PRDs, goals, and milestones
20
- bizar openkan doctor Validate the .ok/ workspace
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
- Shortcuts: \`bizar task\`, \`bizar plan\`, and \`bizar goals\` use this
24
- same OpenKan workspace. Bizar no longer creates a SQLite task ledger or
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 bin = process.env.BIZAR_OPENKAN_BIN || executableOnPath('openkan');
31
- if (!bin) throw new OpenKanError('OPENKAN_NOT_FOUND', 'OpenKan is required. Run `bizar openkan install`.');
32
- const result = spawnSync(bin, args, { cwd: process.cwd(), encoding: 'utf8', shell: false });
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
- print(result);
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} lifecycle\n\n Usage: bizar ${name} <ok ${resource} args...>\n Run \`bizar openkan ${name} --help\` for the canonical OpenKan surface.\n`);
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) {
@@ -3,18 +3,18 @@ import { OpenKanError, runOpenKanOk } from '../openkan.mjs';
3
3
 
4
4
  function help() {
5
5
  process.stdout.write(`
6
- bizar task — OpenKan-backed task lifecycle
6
+ ok task — OpenKan-backed task lifecycle
7
7
 
8
8
  Usage:
9
- bizar task add <title> [--owner agent] [--priority p0|p1|p2|p3]
10
- bizar task list [--status pending|in_progress|review|done|cancelled] [--json]
11
- bizar task show <id> [--json]
12
- bizar task claim <id> --owner <agent> [--lease-ms <ms>]
13
- bizar task heartbeat <id> --owner <agent> [--lease-ms <ms>]
14
- bizar task update <id> --status <status> [--evidence <text>]
15
- bizar task complete <id> --owner <agent> --evidence <text>
16
- bizar task cancel <id> --owner <agent> --reason <text>
17
- bizar task release <id> --owner <agent>
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
 
@@ -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({ mode, dryRun, force: true, yes });
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 })).trim().toLowerCase();
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 answer = (await askText(' Provider URL (for example https://gateway.example/v1): ', { input, output })).trim();
125
- if (!isValidProviderUrl(answer)) {
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 = answer.replace(/\/+$/, '');
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
- return { ok: true, interactive: true, cancelled: false, configured: true, missing: detected.missing };
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
  }
@@ -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('└──────────────────────────────────────────────────────────────────────┘'));