@polderlabs/openkan 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +226 -0
- package/LICENSE +21 -0
- package/README.md +318 -0
- package/agents/openkan.md +254 -0
- package/bin/install-agent.mjs +63 -0
- package/bin/ok.mjs +17 -0
- package/bin/openkan.mjs +10 -0
- package/dist/.claude/skills/ok-planning/SKILL.md +285 -0
- package/dist/.claude/skills/ok-planning/references/integration.md +153 -0
- package/dist/.claude/skills/ok-planning/references/schemas.md +270 -0
- package/dist/.claude/skills/ok-planning/references/workflows.md +185 -0
- package/dist/.claude/skills/ok-planning/scripts/ok-init.sh +14 -0
- package/dist/.claude/skills/ok-planning/scripts/ok-resume.sh +38 -0
- package/dist/.claude/skills/ok-planning/scripts/ok-status.sh +24 -0
- package/dist/agents/openkan.md +254 -0
- package/dist/bin/install-agent.mjs +76 -0
- package/dist/bin/ok-install.js +58 -0
- package/dist/bin/ok.js +138 -0
- package/dist/bin/openkan.js +804 -0
- package/dist/commands/organize.md +15 -0
- package/dist/kanban/agent-profile.js +8 -0
- package/dist/kanban/archive.js +49 -0
- package/dist/kanban/bizar.js +242 -0
- package/dist/kanban/board.js +367 -0
- package/dist/kanban/bulk.js +139 -0
- package/dist/kanban/changelog.js +186 -0
- package/dist/kanban/chat.js +1280 -0
- package/dist/kanban/claude-state.js +974 -0
- package/dist/kanban/comments.js +80 -0
- package/dist/kanban/docs.js +144 -0
- package/dist/kanban/fs.js +163 -0
- package/dist/kanban/git.js +196 -0
- package/dist/kanban/images.js +140 -0
- package/dist/kanban/import.js +295 -0
- package/dist/kanban/inputs.js +94 -0
- package/dist/kanban/insights.js +140 -0
- package/dist/kanban/io.js +75 -0
- package/dist/kanban/mdx-render.js +348 -0
- package/dist/kanban/mdx.js +231 -0
- package/dist/kanban/projects.js +545 -0
- package/dist/kanban/search.js +121 -0
- package/dist/kanban/server.js +3296 -0
- package/dist/kanban/tags.js +124 -0
- package/dist/kanban/template.js +145 -0
- package/dist/kanban/tsx-sandbox.js +187 -0
- package/dist/kanban/watcher.js +270 -0
- package/dist/ok/commands/goal.js +65 -0
- package/dist/ok/commands/index.js +87 -0
- package/dist/ok/commands/init.js +15 -0
- package/dist/ok/commands/plan.js +155 -0
- package/dist/ok/commands/prd.js +202 -0
- package/dist/ok/commands/progress.js +31 -0
- package/dist/ok/commands/task.js +377 -0
- package/dist/ok/ids.js +98 -0
- package/dist/ok/lock.js +156 -0
- package/dist/ok/migrate.js +197 -0
- package/dist/ok/schemas.js +402 -0
- package/dist/ok/storage.js +222 -0
- package/dist/skills/openkan/SKILL.md +111 -0
- package/dist/skills/openkan/agents/openai.yaml +4 -0
- package/dist/skills/openkan/examples/simple-task.mdx +34 -0
- package/dist/skills/openkan/examples/with-ask.mdx +32 -0
- package/dist/skills/openkan/examples/with-choice.mdx +51 -0
- package/dist/skills/openkan/examples/with-preview.mdx +54 -0
- package/dist/skills/openkan/references/api.md +169 -0
- package/dist/skills/openkan/templates/task.mdx +46 -0
- package/dist/web/api.js +257 -0
- package/dist/web/app.js +4251 -0
- package/dist/web/bizar.js +39 -0
- package/dist/web/brand/agent-activity-sprite.svg +1 -0
- package/dist/web/brand/banner-docs.svg +24 -0
- package/dist/web/brand/banner.svg +32 -0
- package/dist/web/brand/empty-sessions.svg +17 -0
- package/dist/web/brand/empty-tasks.svg +17 -0
- package/dist/web/brand/favicon.svg +9 -0
- package/dist/web/brand/infinity-loader-animated.svg +220 -0
- package/dist/web/brand/infinity-loader-spritesheet.svg +230 -0
- package/dist/web/brand/logo-wordmark.svg +10 -0
- package/dist/web/brand/logo.svg +9 -0
- package/dist/web/brand/pixel-infinity-track.svg +1 -0
- package/dist/web/brand/social-card.svg +26 -0
- package/dist/web/changelog-view.js +456 -0
- package/dist/web/charts.js +269 -0
- package/dist/web/chat-sidebar.js +2397 -0
- package/dist/web/chat-status-motion.js +154 -0
- package/dist/web/claude-pane.js +820 -0
- package/dist/web/command-palette.js +381 -0
- package/dist/web/contributors-view.js +317 -0
- package/dist/web/cross-tab.js +102 -0
- package/dist/web/docs-view.js +168 -0
- package/dist/web/experience.css +165 -0
- package/dist/web/goals-view.js +45 -0
- package/dist/web/home-view.js +113 -0
- package/dist/web/images.js +311 -0
- package/dist/web/index.html +485 -0
- package/dist/web/insights.js +217 -0
- package/dist/web/keyboard.js +446 -0
- package/dist/web/mdx-viewer.js +600 -0
- package/dist/web/path-picker.js +787 -0
- package/dist/web/preview-frame.html +187 -0
- package/dist/web/settings.js +582 -0
- package/dist/web/style.css +8545 -0
- package/dist/web/task-view.js +1759 -0
- package/dist/web/vendor/gsap.min.js +11 -0
- package/dist/web/workspace.css +1513 -0
- package/package.json +71 -0
- package/skills/openkan/SKILL.md +111 -0
- package/skills/openkan/agents/openai.yaml +4 -0
- package/skills/openkan/examples/simple-task.mdx +34 -0
- package/skills/openkan/examples/with-ask.mdx +32 -0
- package/skills/openkan/examples/with-choice.mdx +51 -0
- package/skills/openkan/examples/with-preview.mdx +54 -0
- package/skills/openkan/references/api.md +169 -0
- package/skills/openkan/templates/task.mdx +46 -0
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
# Integration — how the pieces fit
|
|
2
|
+
|
|
3
|
+
```
|
|
4
|
+
┌─────────────────────────────────────────────────────────────────┐
|
|
5
|
+
│ Claude Code │
|
|
6
|
+
│ │
|
|
7
|
+
│ ┌─────────────┐ SessionStart ┌──────────────┐ │
|
|
8
|
+
│ │ ok-init.mjs │ ───────────────▶│ bin/ok.ts │ │
|
|
9
|
+
│ │ (hook) │ │ (CLI entry) │ │
|
|
10
|
+
│ └─────────────┘ └──────┬───────┘ │
|
|
11
|
+
│ │ │
|
|
12
|
+
│ ▼ │
|
|
13
|
+
│ ┌─────────────────────┐ │
|
|
14
|
+
│ │ ok/commands/*.ts │ │
|
|
15
|
+
│ │ task / plan / prd │ │
|
|
16
|
+
│ │ index / doctor │ │
|
|
17
|
+
│ └──────────┬──────────┘ │
|
|
18
|
+
│ │ │
|
|
19
|
+
│ ▼ │
|
|
20
|
+
│ ┌─────────────────────┐ │
|
|
21
|
+
│ │ ok/storage.ts │ │
|
|
22
|
+
│ │ ok/lock.ts │ │
|
|
23
|
+
│ │ ok/schemas.ts │ │
|
|
24
|
+
│ └──────────┬──────────┘ │
|
|
25
|
+
└─────────────────────────────────────────┼────────────────────────┘
|
|
26
|
+
│
|
|
27
|
+
▼
|
|
28
|
+
┌─────────────────┐
|
|
29
|
+
│ .ok/ │
|
|
30
|
+
│ config.json │
|
|
31
|
+
│ index.json │
|
|
32
|
+
│ tasks/<id>.json │
|
|
33
|
+
│ plans/<id>.json │
|
|
34
|
+
│ prds/<id>.json │
|
|
35
|
+
│ locks/<id>.lock │
|
|
36
|
+
└────────┬────────┘
|
|
37
|
+
│
|
|
38
|
+
dual-write (every board persist)
|
|
39
|
+
│
|
|
40
|
+
▼
|
|
41
|
+
┌─────────────────┐
|
|
42
|
+
│ kanban/board.ts │
|
|
43
|
+
│ OpenKan engine │
|
|
44
|
+
└─────────────────┘
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Pieces
|
|
48
|
+
|
|
49
|
+
### 1. `bin/ok.ts` (CLI entry)
|
|
50
|
+
|
|
51
|
+
Dispatches to subcommands. Run via `node --experimental-strip-types
|
|
52
|
+
bin/ok.ts …` or through `bin/ok.mjs` which is a small Node launcher.
|
|
53
|
+
|
|
54
|
+
### 2. `ok/commands/*.ts` (subcommands)
|
|
55
|
+
|
|
56
|
+
Each file exports a `runX(args)` function and optionally runs as a
|
|
57
|
+
subprocess via the bottom-of-file `if (import.meta.url === …)` block.
|
|
58
|
+
|
|
59
|
+
- `task.ts` — `ok task add|list|show|update|claim|heartbeat|complete|cancel|release`
|
|
60
|
+
- `plan.ts` — `ok plan add|list|show|update`
|
|
61
|
+
- `prd.ts` — `ok prd add|list|show|update`
|
|
62
|
+
- `index.ts` — `ok index` and `ok doctor`
|
|
63
|
+
- `init.ts` — `ok init`
|
|
64
|
+
|
|
65
|
+
### 3. `ok/storage.ts` (storage layer)
|
|
66
|
+
|
|
67
|
+
Async JSON I/O with atomic writes. Path helpers (`paths(root)`) resolve
|
|
68
|
+
every well-known file/dir under `.ok/`. The core operations are
|
|
69
|
+
typed: `readTask`, `writeTask`, `listTasks`, etc.
|
|
70
|
+
|
|
71
|
+
### 4. `ok/lock.ts` (claim/heartbeat/release)
|
|
72
|
+
|
|
73
|
+
Advisory JSON locks under `.ok/locks/`. Rejects concurrent writes from
|
|
74
|
+
different owners; leases expire after `--lease-ms` (default 1h).
|
|
75
|
+
|
|
76
|
+
### 5. `ok/schemas.ts` (validation)
|
|
77
|
+
|
|
78
|
+
TypeScript types and runtime validation. `ok doctor` runs every file
|
|
79
|
+
through the corresponding validator.
|
|
80
|
+
|
|
81
|
+
### 6. `ok/migrate.ts` (legacy import)
|
|
82
|
+
|
|
83
|
+
One-shot import of `.openkan/tasks.json` and `.openkan/board.json` into
|
|
84
|
+
`.ok/tasks/<id>.json`. Idempotent; second run is a no-op.
|
|
85
|
+
|
|
86
|
+
### 7. `.claude/hooks/ok-init.mjs` (SessionStart hook)
|
|
87
|
+
|
|
88
|
+
Reads `process.env.CLAUDE_PROJECT_DIR` and runs `ok init` if `.ok/`
|
|
89
|
+
doesn't exist. Always exits 0 (hooks never block Claude).
|
|
90
|
+
|
|
91
|
+
### 8. `kanban/board.ts` (OpenKan engine)
|
|
92
|
+
|
|
93
|
+
Persists the OpenKan board to `.ok/board.json` AND mirrors each task
|
|
94
|
+
into `.ok/tasks/<id>.json` (via `mirrorToOkStore`). This is the
|
|
95
|
+
integration point: every board write becomes a planning-system write.
|
|
96
|
+
|
|
97
|
+
### 9. `.claude/skills/ok-planning/` (this skill)
|
|
98
|
+
|
|
99
|
+
The agent-facing surface. Self-contained: a Claude Code agent that loads
|
|
100
|
+
the skill gets everything needed to plan, track, and recover work.
|
|
101
|
+
|
|
102
|
+
## Adding a new subcommand
|
|
103
|
+
|
|
104
|
+
1. Create `ok/commands/<name>.ts` exporting `run<X>(args)` that
|
|
105
|
+
returns `{ code: number }`.
|
|
106
|
+
2. Register it in `bin/ok.ts` under the main switch.
|
|
107
|
+
3. Add a test in `tests/ok-<name>.test.mts` exercising the happy path
|
|
108
|
+
and the most common failures (parse errors, lock contention, schema
|
|
109
|
+
drift).
|
|
110
|
+
4. Mention the subcommand in the skill body and in `docs/OK-PLANNING.md`.
|
|
111
|
+
|
|
112
|
+
Keep one logical operation per commit: the subcommand, its tests, and
|
|
113
|
+
its docs land together.
|
|
114
|
+
|
|
115
|
+
## Why dual-write?
|
|
116
|
+
|
|
117
|
+
The OpenKan engine's existing API surface (`withWrite`, `persist`) is
|
|
118
|
+
unchanged. The mirror write in `mirrorToOkStore` adds a side-effect
|
|
119
|
+
that materialises the planning-system JSON files. Two paths write the
|
|
120
|
+
same data:
|
|
121
|
+
|
|
122
|
+
- `ok task add` → writes `.ok/tasks/<id>.json` directly.
|
|
123
|
+
- OpenKan UI → `kanban/board.ts:persist()` → mirror writes each task.
|
|
124
|
+
|
|
125
|
+
Both paths produce identical `.ok/tasks/<id>.json` content. The mirror
|
|
126
|
+
write is idempotent and safe to call repeatedly.
|
|
127
|
+
|
|
128
|
+
## What if the mirror write fails?
|
|
129
|
+
|
|
130
|
+
The board persist still succeeds; the mirror write is wrapped in
|
|
131
|
+
try/catch and never aborts the engine. If the mirror fails (disk full,
|
|
132
|
+
permission error), the next `ok index` will notice the missing file
|
|
133
|
+
and surface it via `ok doctor`.
|
|
134
|
+
|
|
135
|
+
## Why no SQLite / no central DB?
|
|
136
|
+
|
|
137
|
+
- JSON files are diffable in PRs.
|
|
138
|
+
- One file per entity scales to thousands of tasks per project.
|
|
139
|
+
- No native bindings → runs anywhere Node runs.
|
|
140
|
+
- The `ok doctor` + `ok index` pair gives you DB-equivalent consistency
|
|
141
|
+
checks without the runtime.
|
|
142
|
+
|
|
143
|
+
## Limits
|
|
144
|
+
|
|
145
|
+
The `.ok/` layout assumes:
|
|
146
|
+
|
|
147
|
+
- A single project per directory (the workspace is project-scoped).
|
|
148
|
+
- Trust between writers (advisory locks, not fcntl).
|
|
149
|
+
- File-system atomic rename (every supported platform).
|
|
150
|
+
- Node ≥ 22 (for `--experimental-strip-types`).
|
|
151
|
+
|
|
152
|
+
If any of these assumptions breaks in your environment, the planning
|
|
153
|
+
system is the wrong tool — use a database.
|
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
# Full schema reference — `ok` planning layer
|
|
2
|
+
|
|
3
|
+
This document expands the abridged schemas in `SKILL.md` with field-level
|
|
4
|
+
descriptions, validation rules, and worked examples.
|
|
5
|
+
|
|
6
|
+
All entities follow the same pattern:
|
|
7
|
+
|
|
8
|
+
```jsonc
|
|
9
|
+
{
|
|
10
|
+
"schema": "ok.<entity>.v1",
|
|
11
|
+
// ... entity-specific fields ...
|
|
12
|
+
}
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
The `schema` discriminator is the only identity you should rely on for
|
|
16
|
+
parsing. `ok doctor` checks it before any other validation.
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## Task — `ok.task.v1`
|
|
21
|
+
|
|
22
|
+
An atomic unit of work. Atomicity is intentional: every task should be
|
|
23
|
+
representable in a single commit; multi-commit work is decomposed into
|
|
24
|
+
multiple tasks or promoted to a Plan.
|
|
25
|
+
|
|
26
|
+
```ts
|
|
27
|
+
{
|
|
28
|
+
schema: "ok.task.v1",
|
|
29
|
+
id: string, // "tsk-<nanoid-8>"
|
|
30
|
+
title: string, // 1..200 chars
|
|
31
|
+
description?: string, // optional markdown-lite
|
|
32
|
+
owner?: string, // agent/user; free-form
|
|
33
|
+
status: "pending" | "in_progress" | "review" | "done" | "cancelled",
|
|
34
|
+
priority?: "p0" | "p1" | "p2" | "p3",
|
|
35
|
+
plan?: string, // "pln-<id>"
|
|
36
|
+
prd?: string, // "prd-<id>"
|
|
37
|
+
scopes?: string[], // tags / paths / identifiers
|
|
38
|
+
deps?: string[], // task ids this depends on
|
|
39
|
+
createdAt: string, // ISO
|
|
40
|
+
updatedAt: string, // ISO
|
|
41
|
+
startedAt?: string, // ISO; set on first in_progress
|
|
42
|
+
completedAt?: string, // ISO; set on done/cancelled
|
|
43
|
+
evidence?: string[], // commit shas, file:line, URLs
|
|
44
|
+
acceptance?: string[], // definition-of-done bullets
|
|
45
|
+
}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### Status transitions
|
|
49
|
+
|
|
50
|
+
```
|
|
51
|
+
pending ──claim──▶ in_progress ──update──▶ review ──update──▶ done
|
|
52
|
+
│ │ │
|
|
53
|
+
└────cancel──────────┴──────────────────────┘
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
- `claim` transitions `pending → in_progress` and writes a lock.
|
|
57
|
+
- `update --status review` moves an in-progress task into review.
|
|
58
|
+
- `complete` is the only transition to `done`; it requires `--evidence`.
|
|
59
|
+
- `cancel` is terminal; it requires `--reason` (recorded as evidence).
|
|
60
|
+
|
|
61
|
+
The status enum is the canonical lifecycle indicator. Column placement
|
|
62
|
+
in OpenKan's UI is a presentation concern and is mapped back to `status`
|
|
63
|
+
on read.
|
|
64
|
+
|
|
65
|
+
### Example
|
|
66
|
+
|
|
67
|
+
```json
|
|
68
|
+
{
|
|
69
|
+
"schema": "ok.task.v1",
|
|
70
|
+
"id": "tsk-9brjCkWa",
|
|
71
|
+
"title": "Implement claim helper",
|
|
72
|
+
"description": "Cover happy path, expired lease, and double-claim.",
|
|
73
|
+
"owner": "alice",
|
|
74
|
+
"status": "in_progress",
|
|
75
|
+
"priority": "p1",
|
|
76
|
+
"plan": "pln-7Hg2Vu3W",
|
|
77
|
+
"prd": "prd-T6g9Pz_X",
|
|
78
|
+
"scopes": ["ok/lock.ts", "tests/ok-lock.test.mts"],
|
|
79
|
+
"deps": [],
|
|
80
|
+
"createdAt": "2026-09-04T10:00:00.000Z",
|
|
81
|
+
"updatedAt": "2026-09-04T10:01:30.000Z",
|
|
82
|
+
"startedAt": "2026-09-04T10:01:30.000Z",
|
|
83
|
+
"acceptance": ["two test cases per branch", "no fcntl dependency"]
|
|
84
|
+
}
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
## Plan — `ok.plan.v1`
|
|
90
|
+
|
|
91
|
+
A medium-lived container for a cohesive set of tasks.
|
|
92
|
+
|
|
93
|
+
```ts
|
|
94
|
+
{
|
|
95
|
+
schema: "ok.plan.v1",
|
|
96
|
+
id: string, // "pln-<nanoid-8>"
|
|
97
|
+
title: string,
|
|
98
|
+
summary: string, // 1-2 sentence elevator pitch
|
|
99
|
+
prd?: string, // parent PRD id
|
|
100
|
+
phase?: string, // current phase label (free-form)
|
|
101
|
+
status: "draft" | "active" | "blocked" | "complete" | "abandoned",
|
|
102
|
+
tasks: string[], // ordered task ids
|
|
103
|
+
acceptance: string[], // plan-level DoD
|
|
104
|
+
createdAt: string, // ISO
|
|
105
|
+
updatedAt: string, // ISO
|
|
106
|
+
}
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### Status transitions
|
|
110
|
+
|
|
111
|
+
```
|
|
112
|
+
draft ──update──▶ active ──update──▶ complete
|
|
113
|
+
│ │
|
|
114
|
+
└─update─────────┴──update──▶ abandoned
|
|
115
|
+
│
|
|
116
|
+
└─update──▶ blocked ──update──▶ active
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
- `draft` is the only state in which a plan can be edited without
|
|
120
|
+
justification (acceptance bullets, task list, phase label).
|
|
121
|
+
- `active` plans show up in `ok plan list` defaults.
|
|
122
|
+
- `complete` is terminal.
|
|
123
|
+
|
|
124
|
+
### Example
|
|
125
|
+
|
|
126
|
+
```json
|
|
127
|
+
{
|
|
128
|
+
"schema": "ok.plan.v1",
|
|
129
|
+
"id": "pln-7Hg2Vu3W",
|
|
130
|
+
"title": "v0.1: schemas + storage",
|
|
131
|
+
"summary": "Ship the .ok/ storage layer end-to-end.",
|
|
132
|
+
"prd": "prd-T6g9Pz_X",
|
|
133
|
+
"phase": "M2",
|
|
134
|
+
"status": "active",
|
|
135
|
+
"tasks": ["tsk-9brjCkWa", "tsk-AbCdEfGh", "tsk-IjKlMnOp"],
|
|
136
|
+
"acceptance": ["all schemas validate", "tests green", "doctor returns 0 issues"],
|
|
137
|
+
"createdAt": "2026-09-04T09:55:00.000Z",
|
|
138
|
+
"updatedAt": "2026-09-04T10:01:30.000Z"
|
|
139
|
+
}
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
---
|
|
143
|
+
|
|
144
|
+
## PRD — `ok.prd.v1`
|
|
145
|
+
|
|
146
|
+
A long-horizon Product Requirements Document.
|
|
147
|
+
|
|
148
|
+
```ts
|
|
149
|
+
{
|
|
150
|
+
schema: "ok.prd.v1",
|
|
151
|
+
id: string, // "prd-<nanoid-8>"
|
|
152
|
+
title: string,
|
|
153
|
+
vision: string, // one paragraph
|
|
154
|
+
goals: PrdGoal[], // see below
|
|
155
|
+
nonGoals: string[],
|
|
156
|
+
successMetrics: PrdSuccessMetric[], // see below
|
|
157
|
+
milestones: PrdMilestone[], // see below
|
|
158
|
+
risks: PrdRisk[], // see below
|
|
159
|
+
plans: string[], // plan ids that contribute
|
|
160
|
+
owners: string[], // agents/users
|
|
161
|
+
reviewCadence?: string, // "weekly", "monthly", etc.
|
|
162
|
+
status: "draft" | "active" | "shipped" | "abandoned",
|
|
163
|
+
createdAt: string,
|
|
164
|
+
updatedAt: string,
|
|
165
|
+
nextReviewAt?: string, // ISO
|
|
166
|
+
}
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
### Sub-shapes
|
|
170
|
+
|
|
171
|
+
```ts
|
|
172
|
+
type PrdGoal = { id: string; text: string; status: "open" | "in_progress" | "met" | "dropped" };
|
|
173
|
+
type PrdSuccessMetric = { name: string; target: string; current?: string };
|
|
174
|
+
type PrdMilestone = { id: string; title: string; dueBy?: string; status: "open" | "hit" | "missed" | "dropped" };
|
|
175
|
+
type PrdRisk = { id: string; text: string; severity: "low" | "med" | "high"; mitigation?: string };
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
### Goal / milestone ids
|
|
179
|
+
|
|
180
|
+
Goal ids default to `g1`, `g2`, … in creation order. Milestone ids
|
|
181
|
+
default to `m1`, `m2`, …. Risk ids are auto-assigned by `ok doctor` if
|
|
182
|
+
you write the file by hand.
|
|
183
|
+
|
|
184
|
+
### Example
|
|
185
|
+
|
|
186
|
+
```json
|
|
187
|
+
{
|
|
188
|
+
"schema": "ok.prd.v1",
|
|
189
|
+
"id": "prd-T6g9Pz_X",
|
|
190
|
+
"title": "Self-contained planning workspace",
|
|
191
|
+
"vision": "Every project ships with .ok/ for tasks, plans, and PRDs.",
|
|
192
|
+
"goals": [
|
|
193
|
+
{ "id": "g1", "text": "ship CLI", "status": "met" },
|
|
194
|
+
{ "id": "g2", "text": "ship skill", "status": "open" },
|
|
195
|
+
{ "id": "g3", "text": "ship auto-init", "status": "open" }
|
|
196
|
+
],
|
|
197
|
+
"nonGoals": ["Windows support", "TUI"],
|
|
198
|
+
"successMetrics": [
|
|
199
|
+
{ "name": "active planning workspaces", "target": "20", "current": "0" },
|
|
200
|
+
{ "name": "p50 session bootstrap", "target": "< 200ms", "current": "n/a" }
|
|
201
|
+
],
|
|
202
|
+
"milestones": [
|
|
203
|
+
{ "id": "m1", "title": "v0.1 schema", "dueBy": "2026-10-01T00:00:00Z", "status": "open" },
|
|
204
|
+
{ "id": "m2", "title": "v1.0 launch", "dueBy": "2026-12-15T00:00:00Z", "status": "open" }
|
|
205
|
+
],
|
|
206
|
+
"risks": [
|
|
207
|
+
{ "id": "r1", "text": "agent role confusion", "severity": "med", "mitigation": "skill body disambiguates triggers" }
|
|
208
|
+
],
|
|
209
|
+
"plans": ["pln-7Hg2Vu3W"],
|
|
210
|
+
"owners": ["karen", "todd"],
|
|
211
|
+
"reviewCadence": "weekly",
|
|
212
|
+
"status": "active",
|
|
213
|
+
"createdAt": "2026-09-04T09:00:00.000Z",
|
|
214
|
+
"updatedAt": "2026-09-04T10:00:00.000Z",
|
|
215
|
+
"nextReviewAt": "2026-09-11T09:00:00Z"
|
|
216
|
+
}
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
---
|
|
220
|
+
|
|
221
|
+
## Config — `ok.config.v1`
|
|
222
|
+
|
|
223
|
+
Workspace metadata. One per project. Never carries entity data.
|
|
224
|
+
|
|
225
|
+
```ts
|
|
226
|
+
{
|
|
227
|
+
schema: "ok.config.v1",
|
|
228
|
+
version: 1,
|
|
229
|
+
defaultOwner?: string,
|
|
230
|
+
createdAt: string,
|
|
231
|
+
updatedAt: string,
|
|
232
|
+
}
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
`defaultOwner` is applied to new tasks that omit `--owner`. Useful for
|
|
236
|
+
single-operator projects.
|
|
237
|
+
|
|
238
|
+
---
|
|
239
|
+
|
|
240
|
+
## Index — `ok.index.v1`
|
|
241
|
+
|
|
242
|
+
A compact pointer file for fast listings. Rebuilt via `ok index`.
|
|
243
|
+
|
|
244
|
+
```ts
|
|
245
|
+
{
|
|
246
|
+
schema: "ok.index.v1",
|
|
247
|
+
tasks: IndexEntry[], // {id, status, title, updatedAt}
|
|
248
|
+
plans: IndexEntry[],
|
|
249
|
+
prds: IndexEntry[],
|
|
250
|
+
updatedAt: string,
|
|
251
|
+
}
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
Each entry is sorted by `updatedAt` descending. Agents that just want
|
|
255
|
+
"what is open?" can read this file and skip the per-task JSONs.
|
|
256
|
+
|
|
257
|
+
---
|
|
258
|
+
|
|
259
|
+
## Validation rules summary
|
|
260
|
+
|
|
261
|
+
- All ids must match their kind's regex (`tsk-…`, `pln-…`, `prd-…`).
|
|
262
|
+
- `title` on a Task must be 1..200 chars.
|
|
263
|
+
- Status enums are exact-match; arbitrary strings are rejected.
|
|
264
|
+
- `createdAt` / `updatedAt` must be ISO timestamps (`Date.parse` works).
|
|
265
|
+
- Array fields must be string arrays when present.
|
|
266
|
+
- Unknown fields are ignored (forward-compat); missing required fields
|
|
267
|
+
are rejected.
|
|
268
|
+
|
|
269
|
+
If you write the file by hand and want validation feedback, run
|
|
270
|
+
`ok doctor` after writing.
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
# Workflows — six end-to-end agent flows
|
|
2
|
+
|
|
3
|
+
Each workflow shows the goal, the `ok` commands, and what the agent
|
|
4
|
+
should look for in the output. Run them in any order; each is
|
|
5
|
+
self-contained.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## 1. Start a session and claim work
|
|
10
|
+
|
|
11
|
+
Goal: discover open tasks, take ownership of one before starting.
|
|
12
|
+
|
|
13
|
+
```sh
|
|
14
|
+
# 1. Ensure the workspace exists
|
|
15
|
+
ok init
|
|
16
|
+
|
|
17
|
+
# 2. Find work
|
|
18
|
+
ok task list --status pending --json
|
|
19
|
+
ok task list --status in_progress --json # avoid collisions
|
|
20
|
+
|
|
21
|
+
# 3. Read the chosen task
|
|
22
|
+
ok task show <id> --json
|
|
23
|
+
|
|
24
|
+
# 4. Claim it (writes a lock for 1h default)
|
|
25
|
+
ok task claim <id> --owner <self>
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
What to watch for:
|
|
29
|
+
|
|
30
|
+
- If `ok task claim` prints `locked by …`, the lock is held by another
|
|
31
|
+
agent. Coordinate or wait for the lease to expire (default 1h).
|
|
32
|
+
- `--lease-ms` lets you claim for longer if you know the work is big:
|
|
33
|
+
`ok task claim <id> --owner <self> --lease-ms 3600000`.
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## 2. End a session with concrete evidence
|
|
38
|
+
|
|
39
|
+
Goal: leave the workspace in a state the next agent can pick up.
|
|
40
|
+
|
|
41
|
+
```sh
|
|
42
|
+
# Did you finish?
|
|
43
|
+
ok task complete <id> --owner <self> --evidence "<sha-or-path-or-summary>"
|
|
44
|
+
# or, did you decide to drop?
|
|
45
|
+
ok task cancel <id> --owner <self> --reason "<why>"
|
|
46
|
+
# or, did you just stop without finishing?
|
|
47
|
+
ok task release <id> --owner <self>
|
|
48
|
+
|
|
49
|
+
# Always refresh the index so the next agent sees fresh state
|
|
50
|
+
ok index
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Evidence expectations:
|
|
54
|
+
|
|
55
|
+
- A commit sha (`abc1234`)
|
|
56
|
+
- A `path:line` reference (`ok/lock.ts:60-105`)
|
|
57
|
+
- A URL (`https://…`)
|
|
58
|
+
- A one-line summary of what shipped
|
|
59
|
+
|
|
60
|
+
Anything that lets the next agent (or the user) verify the work without
|
|
61
|
+
chatting with you.
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
## 3. Scope a feature as PRD → Plan → Tasks
|
|
66
|
+
|
|
67
|
+
Goal: capture a long-horizon goal, group it into a milestone, and break
|
|
68
|
+
that into atomic tasks.
|
|
69
|
+
|
|
70
|
+
```sh
|
|
71
|
+
# 1. Define the north star
|
|
72
|
+
ok prd add "<short title>" \
|
|
73
|
+
--vision "<one paragraph north star>" \
|
|
74
|
+
--goals "g1|g2|g3" \
|
|
75
|
+
--milestones "v0.1|v1.0" \
|
|
76
|
+
--non-goals "X|Y" \
|
|
77
|
+
--owners "<agent1>,<agent2>" \
|
|
78
|
+
--review-cadence weekly
|
|
79
|
+
# → prd-AbCdEfGh
|
|
80
|
+
|
|
81
|
+
# 2. Group tasks under the PRD
|
|
82
|
+
ok plan add "v0.1: <milestone>" \
|
|
83
|
+
--prd prd-AbCdEfGh \
|
|
84
|
+
--summary "<elevator pitch>" \
|
|
85
|
+
--acceptance "ship a" "ship b" \
|
|
86
|
+
--tasks tsk-…,tsk-…
|
|
87
|
+
# → pln-XyZ12345
|
|
88
|
+
|
|
89
|
+
# 3. Add tasks with back-link to the plan (optional)
|
|
90
|
+
ok task add "<task>" --plan pln-XyZ12345 --owner <self>
|
|
91
|
+
ok task add "<task>" --prd prd-AbCdEfGh --owner <self>
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Tip: keep PRDs stable across many sessions. Plans are short-lived;
|
|
95
|
+
don't promote plans to PRDs unless the work spans weeks.
|
|
96
|
+
|
|
97
|
+
---
|
|
98
|
+
|
|
99
|
+
## 4. Recover from drift or corruption
|
|
100
|
+
|
|
101
|
+
Goal: bring a `.ok/` tree back to a known-good state.
|
|
102
|
+
|
|
103
|
+
```sh
|
|
104
|
+
# 1. Diagnose
|
|
105
|
+
ok doctor
|
|
106
|
+
|
|
107
|
+
# 2. If the index is stale, rebuild
|
|
108
|
+
ok index
|
|
109
|
+
|
|
110
|
+
# 3. If a single file is corrupt, fix or remove it
|
|
111
|
+
rm .ok/tasks/tsk-bad.json
|
|
112
|
+
ok index
|
|
113
|
+
ok doctor
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
Common drift scenarios:
|
|
117
|
+
|
|
118
|
+
- **Stale lock** — `ok task claim <id> --owner <self>` after the lease
|
|
119
|
+
expires, or `ok task release <id> --owner <stale-owner>` if you know
|
|
120
|
+
the owner.
|
|
121
|
+
- **Index out of sync** — `ok index` rebuilds it from the filesystem.
|
|
122
|
+
- **Schema version mismatch** — bump the schema field and add a
|
|
123
|
+
migration in `ok/storage.ts`; never silently drop fields.
|
|
124
|
+
- **Partial write leftover** — `.tmp-<pid>-<ts>` files are safe to
|
|
125
|
+
delete; `ok init` will recreate them as needed.
|
|
126
|
+
|
|
127
|
+
---
|
|
128
|
+
|
|
129
|
+
## 5. Hand off to another agent
|
|
130
|
+
|
|
131
|
+
Goal: pass ownership cleanly without losing the lock or the work.
|
|
132
|
+
|
|
133
|
+
```sh
|
|
134
|
+
# Releasing agent
|
|
135
|
+
ok task update <id> --evidence "handoff to <new-agent>: <summary of state>"
|
|
136
|
+
ok task release <id> --owner <self>
|
|
137
|
+
|
|
138
|
+
# Picking-up agent
|
|
139
|
+
ok task claim <id> --owner <new-agent>
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
If the handover is bigger than a task (a whole plan or PRD), use
|
|
143
|
+
`--append-plan` on the PRD to record the new plan, and update
|
|
144
|
+
`prd.owners` via a JSON edit (or wait for the `ok prd update --owners`
|
|
145
|
+
flag to land).
|
|
146
|
+
|
|
147
|
+
---
|
|
148
|
+
|
|
149
|
+
## 6. Migrate from a legacy `.openkan/` workspace
|
|
150
|
+
|
|
151
|
+
Goal: bring an existing OpenKan board under the new `.ok/` planning
|
|
152
|
+
layer.
|
|
153
|
+
|
|
154
|
+
```sh
|
|
155
|
+
ok migrate-from-openkan # one-shot, idempotent
|
|
156
|
+
ok doctor # validate the result
|
|
157
|
+
ok index # rebuild the index
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
Status mapping (legacy → planning):
|
|
161
|
+
|
|
162
|
+
| Legacy state | Planning status |
|
|
163
|
+
|--------------|-----------------|
|
|
164
|
+
| `done` | `done` |
|
|
165
|
+
| `running`, `waiting-for-input` | `in_progress` |
|
|
166
|
+
| `cancelled`, `failed` | `cancelled` |
|
|
167
|
+
| column `review` (state idle) | `review` |
|
|
168
|
+
| column `doing` (state idle) | `in_progress` |
|
|
169
|
+
| otherwise | `pending` |
|
|
170
|
+
|
|
171
|
+
Archived tasks become `cancelled`. Priority `urgent` → `p0`,
|
|
172
|
+
`normal` → `p2`, `low` → `p3`.
|
|
173
|
+
|
|
174
|
+
If a task has both a `tasks.json` and a `board.json` entry, the
|
|
175
|
+
`tasks.json` version wins (it's the older source of truth).
|
|
176
|
+
|
|
177
|
+
---
|
|
178
|
+
|
|
179
|
+
## Diagnostic commands
|
|
180
|
+
|
|
181
|
+
- `ok help` — usage summary.
|
|
182
|
+
- `ok doctor` — schema validation across the tree.
|
|
183
|
+
- `ok index` — rebuild `.ok/index.json` from the filesystem.
|
|
184
|
+
- `ok task list --json | jq '. | length'` — quick counts.
|
|
185
|
+
- `ok task show <id> --json` — full record.
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# ok-init.sh — bash wrapper that runs `ok init` and prints the layout.
|
|
3
|
+
#
|
|
4
|
+
# Usage: ok-init.sh [project-root]
|
|
5
|
+
# (defaults to the current directory)
|
|
6
|
+
|
|
7
|
+
set -euo pipefail
|
|
8
|
+
|
|
9
|
+
ROOT="${1:-$(pwd)}"
|
|
10
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
11
|
+
# Resolve the repo root (the directory that contains bin/ok.ts).
|
|
12
|
+
REPO_ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)"
|
|
13
|
+
|
|
14
|
+
( cd "$ROOT" && node --experimental-strip-types "$REPO_ROOT/bin/ok.ts" init )
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# ok-resume.sh — bash wrapper that prints an at-a-glance summary of what's
|
|
3
|
+
# open, in progress, and shippable.
|
|
4
|
+
#
|
|
5
|
+
# Reads .ok/index.json if it exists, else rebuilds it on the fly.
|
|
6
|
+
# Sorts each section by `updatedAt` descending.
|
|
7
|
+
|
|
8
|
+
set -euo pipefail
|
|
9
|
+
|
|
10
|
+
ROOT="${1:-$(pwd)}"
|
|
11
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
12
|
+
REPO_ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)"
|
|
13
|
+
|
|
14
|
+
cd "$ROOT"
|
|
15
|
+
|
|
16
|
+
INDEX=".ok/index.json"
|
|
17
|
+
if [[ ! -f "$INDEX" ]]; then
|
|
18
|
+
node --experimental-strip-types "$REPO_ROOT/bin/ok.ts" index >/dev/null
|
|
19
|
+
fi
|
|
20
|
+
|
|
21
|
+
if command -v jq >/dev/null 2>&1; then
|
|
22
|
+
tasks_total=$(jq '.tasks | length' "$INDEX")
|
|
23
|
+
plans_total=$(jq '.plans | length' "$INDEX")
|
|
24
|
+
prds_total=$(jq '.prds | length' "$INDEX")
|
|
25
|
+
tasks_open=$(jq '[.tasks[] | select(.status == "pending" or .status == "in_progress" or .status == "review")] | length' "$INDEX")
|
|
26
|
+
plans_active=$(jq '[.plans[] | select(.status == "active")] | length' "$INDEX")
|
|
27
|
+
prds_active=$(jq '[.prds[] | select(.status == "active")] | length' "$INDEX")
|
|
28
|
+
|
|
29
|
+
echo "tasks: $tasks_open open / $tasks_total total"
|
|
30
|
+
echo "plans: $plans_active active / $plans_total total"
|
|
31
|
+
echo "prds: $prds_active active / $prds_total total"
|
|
32
|
+
echo "---"
|
|
33
|
+
echo "most-recently-updated items:"
|
|
34
|
+
jq -r '((.tasks + .plans + .prds) | sort_by(-.updatedAt) | .[0:5] | .[] | " [\(.status)] \(.id) \(.title)")' "$INDEX"
|
|
35
|
+
else
|
|
36
|
+
echo "open tasks: $(node --experimental-strip-types "$REPO_ROOT/bin/ok.ts" task list --status pending --json | wc -l) bytes of JSON"
|
|
37
|
+
echo "(install jq for the full breakdown)"
|
|
38
|
+
fi
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# ok-status.sh — bash wrapper that prints a quick health summary.
|
|
3
|
+
#
|
|
4
|
+
# Runs `ok doctor && ok task list --status pending --json | jq '. | length'`.
|
|
5
|
+
# Falls back gracefully when `jq` is not installed.
|
|
6
|
+
|
|
7
|
+
set -euo pipefail
|
|
8
|
+
|
|
9
|
+
ROOT="${1:-$(pwd)}"
|
|
10
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
11
|
+
REPO_ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)"
|
|
12
|
+
|
|
13
|
+
cd "$ROOT"
|
|
14
|
+
|
|
15
|
+
node --experimental-strip-types "$REPO_ROOT/bin/ok.ts" doctor
|
|
16
|
+
|
|
17
|
+
if command -v jq >/dev/null 2>&1; then
|
|
18
|
+
open=$(node --experimental-strip-types "$REPO_ROOT/bin/ok.ts" task list --status pending --json | jq 'length')
|
|
19
|
+
in_progress=$(node --experimental-strip-types "$REPO_ROOT/bin/ok.ts" task list --status in_progress --json | jq 'length')
|
|
20
|
+
review=$(node --experimental-strip-types "$REPO_ROOT/bin/ok.ts" task list --status review --json | jq 'length')
|
|
21
|
+
echo "open tasks: $open in progress: $in_progress in review: $review"
|
|
22
|
+
else
|
|
23
|
+
echo "(install jq for full counts; `ok task list --status pending --json` returns the array)"
|
|
24
|
+
fi
|