@polderlabs/bizar 10.29.0 → 10.29.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.
@@ -1,6 +1,12 @@
1
1
  import { spawnSync } from 'node:child_process';
2
+ import { dirname, join, resolve as resolvePath } from 'node:path';
3
+ import { fileURLToPath } from 'node:url';
2
4
  import { ensureOpenKanProject, installOpenKanPromise, OpenKanError, resolveOpenKanDashboard, runOpenKanOk } from '../openkan.mjs';
3
5
 
6
+ const HERE = dirname(fileURLToPath(import.meta.url));
7
+ const MIGRATE_TASKS = join(HERE, '..', '..', 'scripts', 'openkan', 'migrate-tasks-to-v2.mts');
8
+ const MIGRATE_BOARD = join(HERE, '..', '..', 'scripts', 'openkan', 'migrate-board-to-v2.mts');
9
+
4
10
  function print(result) {
5
11
  if (result.stdout) process.stdout.write(result.stdout);
6
12
  if (result.stderr) process.stderr.write(result.stderr);
@@ -18,6 +24,10 @@ Usage:
18
24
  ok plan <add|list|show|update> Manage plans and phases
19
25
  ok prd <add|list|show|update> Manage PRDs, goals, and milestones
20
26
  ok doctor Validate the .ok/ workspace
27
+ bizar openkan project clean [--apply|--all|--dry-run] Clean project workspace
28
+ bizar openkan board delete <id> Delete a board
29
+ bizar openkan migrate [--apply] [--tasks|--board] Migrate v1 tasks/board to v2 layout
30
+ (default: dry-run; --apply to apply)
21
31
  bizar openkan dashboard [args...] Forward to the OpenKan dashboard CLI
22
32
  (legacy openkan.mjs on pre-v0.5.0
23
33
  releases; ok serve on v0.5.0+ where
@@ -30,6 +40,25 @@ feature/progress files for live planning.
30
40
  `);
31
41
  }
32
42
 
43
+ /**
44
+ * Run one of the vendored OpenKan migration scripts. The scripts are
45
+ * `.mts` files that need `--experimental-strip-types` because they
46
+ * preserve the upstream TypeScript source verbatim from the v0.7.0
47
+ * tag. See `scripts/openkan/` for the vendored files.
48
+ */
49
+ function runMigrateScript(scriptPath, args) {
50
+ const resolved = resolvePath(scriptPath);
51
+ const result = spawnSync(process.execPath, ['--experimental-strip-types', resolved, ...args], {
52
+ cwd: process.cwd(),
53
+ encoding: 'utf8',
54
+ shell: false,
55
+ stdio: ['ignore', 'pipe', 'pipe'],
56
+ });
57
+ if (result.stdout) process.stdout.write(result.stdout);
58
+ if (result.stderr) process.stderr.write(result.stderr);
59
+ return result.status ?? 1;
60
+ }
61
+
33
62
  function runDashboard(args) {
34
63
  const launcher = resolveOpenKanDashboard();
35
64
  // OpenKan v0.5.0 dropped the legacy `openkan` dashboard launcher. The
@@ -74,6 +103,48 @@ export async function run(name, args, isHelpRequest) {
74
103
  print(runOpenKanOk([subcommand, ...rest]));
75
104
  return true;
76
105
  }
106
+ if (subcommand === 'project') {
107
+ // Forward project subcommands (e.g., project clean)
108
+ print(runOpenKanOk([subcommand, ...rest]));
109
+ return true;
110
+ }
111
+ if (subcommand === 'board') {
112
+ // Forward board subcommands (e.g., board delete)
113
+ print(runOpenKanOk([subcommand, ...rest]));
114
+ return true;
115
+ }
116
+ if (subcommand === 'migrate') {
117
+ // Migrate v1 tasks/board to the v2 layout that OpenKan 0.7.0 ships.
118
+ // Default is dry-run; --apply actually moves files. --tasks / --board
119
+ // narrow scope. The vendored scripts live under scripts/openkan/ and
120
+ // run via --experimental-strip-types because they're .mts.
121
+ const apply = rest.includes('--apply');
122
+ const tasksOnly = rest.includes('--tasks');
123
+ const boardOnly = rest.includes('--board');
124
+ if (!apply) {
125
+ process.stdout.write('Running in dry-run mode. Pass --apply to actually migrate.\n\n');
126
+ }
127
+ let exitCode = 0;
128
+ if (!boardOnly) {
129
+ process.stdout.write('=== Migrating tasks (.ok/tasks/<id>.json → <id>/task.json) ===\n');
130
+ const code = runMigrateScript(MIGRATE_TASKS, apply ? [] : ['--dry-run']);
131
+ if (code !== 0) exitCode = code;
132
+ }
133
+ if (!tasksOnly) {
134
+ process.stdout.write('\n=== Migrating board.json → per-task directories ===\n');
135
+ const code = runMigrateScript(MIGRATE_BOARD, apply ? [] : ['--dry-run']);
136
+ if (code !== 0) exitCode = code;
137
+ }
138
+ if (exitCode === 0) {
139
+ process.stdout.write(apply
140
+ ? '\n✓ Migration complete.\n'
141
+ : '\n✓ Dry-run complete. Re-run with --apply to execute.\n');
142
+ } else {
143
+ process.stderr.write('\n✗ Migration completed with errors. See output above.\n');
144
+ }
145
+ process.exitCode = exitCode;
146
+ return true;
147
+ }
77
148
  if (subcommand === 'dashboard') { runDashboard(rest); return true; }
78
149
  help();
79
150
  process.exitCode = 2;
@@ -6,7 +6,7 @@ function help() {
6
6
  ok task — OpenKan-backed task lifecycle
7
7
 
8
8
  Usage:
9
- ok task add <title> [--owner agent] [--priority p0|p1|p2|p3]
9
+ ok task add <title> [--owner agent] [--priority low|normal|high|urgent]
10
10
  ok task list [--status pending|in_progress|review|done|cancelled] [--json]
11
11
  ok task show <id> [--json]
12
12
  ok task claim <id> --owner <agent> [--lease-ms <ms>]
@@ -5,8 +5,11 @@
5
5
  * that delegates to the provisioner.
6
6
  */
7
7
 
8
+ import chalk from 'chalk';
9
+
8
10
  import { runProvision, forceCleanInstall, clearSavedEnv } from '../provision.mjs';
9
11
  import { runDoctor } from '../doctor.mjs';
12
+ import { runStatuslineInstall } from '../commands/statusline.mjs';
10
13
  import { showBanner, sectionHeading } from './banner.mjs';
11
14
  import { printInstallLocations } from './paths.mjs';
12
15
  import { runInteractiveSetup } from './interactive-setup.mjs';
@@ -32,9 +35,19 @@ import { runInteractiveSetup } from './interactive-setup.mjs';
32
35
  * @param {boolean} [opts.quiet] - Only print the location card
33
36
  * @param {string} [opts.mode] - 'install' | 'update'
34
37
  * @param {boolean} [opts.yes] - assume yes for any non-destructive prompts
38
+ * @param {Function} [opts.statuslineInstall] - injectable statusline installer for tests
39
+ * @param {Function} [opts.provision] - injectable provisioner for tests
35
40
  */
36
41
  export async function runInstaller(opts = {}) {
37
- const { dryRun = false, force = false, quiet = false, mode = 'install', yes = false } = opts;
42
+ const {
43
+ dryRun = false,
44
+ force = false,
45
+ quiet = false,
46
+ mode = 'install',
47
+ yes = false,
48
+ statuslineInstall = runStatuslineInstall,
49
+ provision = runProvision,
50
+ } = opts;
38
51
 
39
52
  if (quiet) {
40
53
  printInstallLocations({ dryRun, force });
@@ -78,7 +91,7 @@ export async function runInstaller(opts = {}) {
78
91
  // Always pass `force: true` downstream so `runProvision` re-emits the
79
92
  // template-owned keys (permissions.allow wildcards, mcpServers, hooks)
80
93
  // into the freshly-empty settings file.
81
- const provisionResult = await runProvision({
94
+ const provisionResult = await provision({
82
95
  mode,
83
96
  dryRun,
84
97
  force: true,
@@ -87,6 +100,22 @@ export async function runInstaller(opts = {}) {
87
100
  initializeOpenKanProject: interactive?.initializeOpenKanProject === true,
88
101
  });
89
102
 
103
+ // Auto-install statusline (v10.29.2+). Skipped on dry-run, non-fatal on failure.
104
+ // Idempotent: re-running is safe — updateStatuslineSettings re-writes the same field.
105
+ if (!dryRun) {
106
+ try {
107
+ const statuslineResult = await statuslineInstall([]);
108
+ if (statuslineResult?.ok) {
109
+ provisionResult.statuslineInstalled = true;
110
+ } else {
111
+ console.log(chalk.yellow(` ! statusline auto-install failed: ${statuslineResult?.error || 'unknown'}`));
112
+ }
113
+ } catch (err) {
114
+ // Non-fatal: the user can still run `bizar statusline install` manually.
115
+ console.log(chalk.yellow(` ! statusline auto-install skipped: ${err?.message || err}`));
116
+ }
117
+ }
118
+
90
119
  // F-183 — post-install health check. Surfaced as a warning rather
91
120
  // than a hard failure so a forced install that completes without
92
121
  // error still reports its doctor summary; the operator decides
@@ -1,18 +1,33 @@
1
1
  /** Read-only `.ok/` adapter used by Bizar hooks and control snapshots. */
2
- import { existsSync, readFileSync, readdirSync } from 'node:fs';
2
+ import { existsSync, lstatSync, readFileSync, readdirSync } from 'node:fs';
3
3
  import { isAbsolute, join, relative, resolve } from 'node:path';
4
4
 
5
5
  function readJson(path, fallback = null) {
6
6
  try { return JSON.parse(readFileSync(path, 'utf8')); } catch { return fallback; }
7
7
  }
8
8
 
9
+ function isDirectory(path) {
10
+ try { return lstatSync(path).isDirectory(); } catch { return false; }
11
+ }
12
+
13
+ /** Emits a warning if legacy v1 task files are detected. */
14
+ function warnOnLegacyTasks(dir) {
15
+ try {
16
+ const files = readdirSync(dir).filter((name) => name.endsWith('.json'));
17
+ if (files.length > 0) {
18
+ process.stderr.write(`[openkan-store] Warning: Found ${files.length} legacy v1 task file(s) in ${dir}. Consider running 'ok project migrate' to upgrade to v2 layout.\n`);
19
+ }
20
+ } catch { /* dir doesn't exist */ }
21
+ }
22
+
9
23
  export function openKanDir(root = process.cwd()) { return join(resolve(root), '.ok'); }
10
24
 
11
25
  export function listOpenKanTasks(root = process.cwd()) {
12
26
  const dir = join(openKanDir(root), 'tasks');
13
27
  if (!existsSync(dir)) return [];
14
- return readdirSync(dir).filter((name) => name.endsWith('.json')).sort()
15
- .map((name) => readJson(join(dir, name))).filter((task) => task?.schema === 'ok.task.v1');
28
+ warnOnLegacyTasks(dir);
29
+ return readdirSync(dir).filter((name) => isDirectory(join(dir, name))).sort()
30
+ .map((name) => readJson(join(dir, name, 'task.json'))).filter((task) => task?.schema === 'ok.task.v2');
16
31
  }
17
32
 
18
33
  export function listOpenKanPlans(root = process.cwd()) {
@@ -38,7 +38,7 @@ Use Autopilot for a clear, non-trivial outcome that should be delivered locally
38
38
  ```sh
39
39
  # default profile → bounded task-wave execution, ≤3 parallel agents
40
40
  ok plan add "$TASK_GOAL" --summary "Autopilot plan-build-qa run" --json
41
- ok task add "research/spec" --plan "$PLAN_ID" --priority p1 --json
41
+ ok task add "research/spec" --plan "$PLAN_ID" --priority normal --json
42
42
  ```
43
43
 
44
44
  Examples: `/autopilot implement the task` selects `default`; `/autopilot --workflow plan-build-qa implement the task` selects the plan-led profile. Reject unknown workflow names instead of silently substituting another profile.
@@ -52,7 +52,7 @@ For every transition, re-read task/plan state and use the current values. Record
52
52
  ```sh
53
53
  ok task update "$TASK_ID" --status in_progress --json
54
54
  ok task complete "$TASK_ID" --evidence "$BOUNDED_EVIDENCE" --json
55
- ok task add "<next-stage>" --plan "$PLAN_ID" --priority p1 --json
55
+ ok task add "<next-stage>" --plan "$PLAN_ID" --priority normal --json
56
56
  ```
57
57
 
58
58
  Run the stages in order. Each stage is a task under the plan; advancing the run means completing the current task with evidence and creating the next.
@@ -82,7 +82,7 @@ ok plan list --json
82
82
  ok prd list --json
83
83
  # When no plan exists:
84
84
  ok plan add "$ARGUMENTS" --summary "bizplan plan-build" --json
85
- ok task add "research/spec" --plan "$PLAN_ID" --priority p1 --json
85
+ ok task add "research/spec" --plan "$PLAN_ID" --priority normal --json
86
86
  ```
87
87
 
88
88
  Ground the specification in repository evidence. For external APIs, frameworks,
@@ -25,7 +25,7 @@ ok plan list --json
25
25
  ok plan show "$ACTIVE_PLAN_ID" --json
26
26
  # When no plan exists:
27
27
  ok plan add "$ARGUMENTS" --summary "Ralph persistent loop" --json
28
- ok task add "research/spec" --plan "$PLAN_ID" --priority p1 --json
28
+ ok task add "research/spec" --plan "$PLAN_ID" --priority normal --json
29
29
  ```
30
30
 
31
31
  At each phase boundary, re-read state and advance using the current task identity plus compact proving evidence:
@@ -33,7 +33,7 @@ At each phase boundary, re-read state and advance using the current task identit
33
33
  ```sh
34
34
  ok task update "$TASK_ID" --status in_progress --json
35
35
  ok task complete "$TASK_ID" --evidence "$BOUNDED_EVIDENCE" --json
36
- ok task add "<next-stage>" --plan "$PLAN_ID" --priority p1 --json
36
+ ok task add "<next-stage>" --plan "$PLAN_ID" --priority normal --json
37
37
  ```
38
38
 
39
39
  ## Loop
@@ -65,7 +65,7 @@ ok task list --json
65
65
  # When no PRD exists:
66
66
  ok prd add "$ARGUMENTS" --review-cadence weekly --json
67
67
  ok plan add "<objective>" --prd "$PRD_ID" --json
68
- ok task add "research/spec" --plan "$PLAN_ID" --priority p1 --json
68
+ ok task add "research/spec" --plan "$PLAN_ID" --priority normal --json
69
69
  ```
70
70
 
71
71
  `--mode aggregate|per-story` is a leading selector on the ultragoal CLI
@@ -90,7 +90,7 @@ sum of completed subgoals reaches the configured `completionThreshold`
90
90
  (default: `1.0`).
91
91
 
92
92
  ```sh
93
- ok task add "<summary>" --plan "$PLAN_ID" --priority p1 \
93
+ ok task add "<summary>" --plan "$PLAN_ID" --priority normal \
94
94
  --acceptance "weight=0.25" --description "<summary>" --json
95
95
  ```
96
96
 
@@ -108,7 +108,7 @@ its own `done` state; partial completion stays at `review` or
108
108
  `checkpointing`.
109
109
 
110
110
  ```sh
111
- ok task add "<story summary>" --plan "$PLAN_ID" --priority p1 \
111
+ ok task add "<story summary>" --plan "$PLAN_ID" --priority normal \
112
112
  --acceptance "story=true,story-id=<sid>" --description "<story summary>" --json
113
113
  ```
114
114
 
@@ -27,7 +27,7 @@ Resume an existing plan when present. If no plan exists, create a `plan-build-qa
27
27
  ```sh
28
28
  ok plan show "$ACTIVE_PLAN_ID" --json
29
29
  ok plan add "$ARGUMENTS" --summary "UltraQA plan-build-qa run" --json
30
- ok task add "qa" --plan "$PLAN_ID" --priority p1 --json
30
+ ok task add "qa" --plan "$PLAN_ID" --priority normal --json
31
31
  ```
32
32
 
33
33
  ## Bounded cycle
@@ -45,7 +45,7 @@ When QA is green, re-read state and advance:
45
45
  ```sh
46
46
  ok task update "$TASK_ID" --status review --json
47
47
  ok task complete "$TASK_ID" --evidence "$BOUNDED_EVIDENCE" --json
48
- ok task add "validate" --plan "$PLAN_ID" --priority p1 --json
48
+ ok task add "validate" --plan "$PLAN_ID" --priority normal --json
49
49
  ```
50
50
 
51
51
  If the cycle limit is reached, run `ok task cancel "$TASK_ID" --reason "$REASON" --json` and mark the owning plan `abandoned`. Never hide skipped checks or flaky results. After QA, functional, security/policy, and code-quality validation remain required. UltraQA never auto-commits, pushes, publishes, releases, deploys, changes credentials/access, or uses daemon/tmux or a general memory/wiki service.
@@ -23,7 +23,7 @@ ok plan list --json
23
23
  ok plan show "$ACTIVE_PLAN_ID" --json
24
24
  # When no plan exists:
25
25
  ok plan add "$ARGUMENTS" --summary "Ultrawork bounded parallel waves" --json
26
- ok task add "research/spec" --plan "$PLAN_ID" --priority p1 --json
26
+ ok task add "research/spec" --plan "$PLAN_ID" --priority normal --json
27
27
  ```
28
28
 
29
29
  Advance only after re-reading status and collecting bounded evidence:
@@ -31,7 +31,7 @@ Advance only after re-reading status and collecting bounded evidence:
31
31
  ```sh
32
32
  ok task update "$TASK_ID" --status in_progress --json
33
33
  ok task complete "$TASK_ID" --evidence "$BOUNDED_EVIDENCE" --json
34
- ok task add "<next-stage>" --plan "$PLAN_ID" --priority p1 --json
34
+ ok task add "<next-stage>" --plan "$PLAN_ID" --priority normal --json
35
35
  ```
36
36
 
37
37
  ## Wave protocol
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@polderlabs/bizar",
3
- "version": "10.29.0",
3
+ "version": "10.29.2",
4
4
  "description": "Autonomous, human-in-the-loop multi-agent harness for Claude Code with guarded workflows, typed SDK primitives, and MCP tools.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -250,7 +250,22 @@ export function spawnExecutorTask(plan, opts = {}) {
250
250
  });
251
251
  const tasksDir = join(okDir, "tasks");
252
252
  mkdirSync(tasksDir, { recursive: true });
253
- atomicWrite(join(tasksDir, `${id}.json`), JSON.stringify(task, null, 2) + "\n");
253
+ // OpenKan 0.7.0 stores tasks under .ok/tasks/<id>/task.json with schema
254
+ // ok.task.v2. Write a minimal v2 record so the new layout is correct
255
+ // out of the box; downstream surfaces hydrate the full v2 superset
256
+ // when the executor claims the task.
257
+ const taskDir = join(tasksDir, id);
258
+ mkdirSync(taskDir, { recursive: true });
259
+ const v2Task = {
260
+ schema: "ok.task.v2",
261
+ id,
262
+ planId: plan.id,
263
+ status: "claimed",
264
+ assignedAt,
265
+ ...(opts.assignee ? { assignee: opts.assignee } : {}),
266
+ links: { planId: plan.id },
267
+ };
268
+ atomicWrite(join(taskDir, "task.json"), JSON.stringify(v2Task, null, 2) + "\n");
254
269
  return task;
255
270
  }
256
271
  //# sourceMappingURL=bizplan.js.map
@@ -1,5 +1,5 @@
1
1
  /**
2
2
  * SDK version constant. Keep synchronized with the workspace package versions.
3
3
  */
4
- export declare const SDK_VERSION: "10.29.0";
4
+ export declare const SDK_VERSION: "10.29.2";
5
5
  //# sourceMappingURL=version.d.ts.map
@@ -1,5 +1,5 @@
1
1
  /**
2
2
  * SDK version constant. Keep synchronized with the workspace package versions.
3
3
  */
4
- export const SDK_VERSION = "10.29.0";
4
+ export const SDK_VERSION = "10.29.2";
5
5
  //# sourceMappingURL=version.js.map
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@polderlabs/bizar-sdk",
3
- "version": "10.29.0",
3
+ "version": "10.29.2",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",