@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.
- package/cli/commands/openkan.mjs +71 -0
- package/cli/commands/task.mjs +1 -1
- package/cli/install/index.mjs +31 -2
- package/cli/openkan-store.mjs +18 -3
- package/config/skills/autopilot/SKILL.md +2 -2
- package/config/skills/bizplan/SKILL.md +1 -1
- package/config/skills/ralph/SKILL.md +2 -2
- package/config/skills/ultragoal/SKILL.md +3 -3
- package/config/skills/ultraqa/SKILL.md +2 -2
- package/config/skills/ultrawork/SKILL.md +2 -2
- package/package.json +1 -1
- package/packages/sdk/dist/handoff/bizplan.js +16 -1
- 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/cli/commands/openkan.mjs
CHANGED
|
@@ -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;
|
package/cli/commands/task.mjs
CHANGED
|
@@ -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
|
|
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>]
|
package/cli/install/index.mjs
CHANGED
|
@@ -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 {
|
|
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
|
|
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
|
package/cli/openkan-store.mjs
CHANGED
|
@@ -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
|
-
|
|
15
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
34
|
+
ok task add "<next-stage>" --plan "$PLAN_ID" --priority normal --json
|
|
35
35
|
```
|
|
36
36
|
|
|
37
37
|
## Wave protocol
|
package/package.json
CHANGED
|
@@ -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
|
-
|
|
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
|