@starlove/orbit 1.0.1 → 1.0.3
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/.dockerignore +0 -1
- package/Dockerfile +1 -15
- package/README.md +6 -5
- package/dist/full/version.json +2 -2
- package/docs/MCP_SETUP.md +1 -1
- package/package.json +1 -3
- package/src/cli/orbit.js +23 -11
- package/src/core/boards.js +59 -14
- package/src/core/provision-repo-board.js +8 -2
- package/src/core/seed.js +12 -3
- package/pnpm-workspace.yaml +0 -2
package/.dockerignore
CHANGED
package/Dockerfile
CHANGED
|
@@ -1,19 +1,5 @@
|
|
|
1
1
|
# syntax=docker/dockerfile:1
|
|
2
2
|
|
|
3
|
-
FROM node:22-slim AS build
|
|
4
|
-
WORKDIR /app
|
|
5
|
-
|
|
6
|
-
ENV COREPACK_ENABLE_DOWNLOAD_PROMPT=0
|
|
7
|
-
|
|
8
|
-
RUN corepack enable && corepack prepare pnpm@10.20.0 --activate
|
|
9
|
-
|
|
10
|
-
COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./
|
|
11
|
-
RUN pnpm install --frozen-lockfile
|
|
12
|
-
|
|
13
|
-
COPY build.mjs ./
|
|
14
|
-
COPY public ./public
|
|
15
|
-
RUN pnpm run build:full
|
|
16
|
-
|
|
17
3
|
FROM node:22-slim AS runtime
|
|
18
4
|
WORKDIR /app
|
|
19
5
|
|
|
@@ -25,7 +11,7 @@ COPY package.json ./
|
|
|
25
11
|
COPY SKILL-ORBIT.md ./SKILL-ORBIT.md
|
|
26
12
|
COPY docs ./docs
|
|
27
13
|
COPY src ./src
|
|
28
|
-
COPY
|
|
14
|
+
COPY dist/full ./dist/full
|
|
29
15
|
|
|
30
16
|
VOLUME ["/data"]
|
|
31
17
|
EXPOSE 3337
|
package/README.md
CHANGED
|
@@ -37,6 +37,7 @@ From the repo you want a board for:
|
|
|
37
37
|
cd path/to/your-app
|
|
38
38
|
orbit init # creates .orbit/board.db + SKILL-ORBIT.md + AGENTS.md
|
|
39
39
|
orbit init --example # optional: add onboarding example tickets
|
|
40
|
+
orbit init --no-ai # optional: create or update the board with AI disabled
|
|
40
41
|
```
|
|
41
42
|
|
|
42
43
|
Start one runtime and keep using it:
|
|
@@ -67,9 +68,9 @@ Wipe a single board and start over with `orbit reset` (deletes `.orbit/`, `SKILL
|
|
|
67
68
|
|
|
68
69
|
## Use with AI
|
|
69
70
|
|
|
70
|
-
Orbit ships with everything an agent needs to claim work, read tickets, and write back implementation notes
|
|
71
|
+
Orbit ships with everything an agent needs to claim work, read tickets, and write back implementation notes. New boards have AI collaboration enabled by default; pass `orbit init --no-ai` or turn off **Enable AI** in Settings → AI if you want a human-only board.
|
|
71
72
|
|
|
72
|
-
1. **
|
|
73
|
+
1. **Confirm AI is enabled.** Open Settings (gear icon) → **AI** tab → toggle **Enable AI** if needed. Orbit provisions the `AI Ready`, `In Progress`, and `Review` lanes if they're missing, surfaces the agent-context fields, and reveals the MCP setup snippet.
|
|
73
74
|
2. **Fill in agent context.** Settings → AI → *Agent Instructions* is the project-level briefing every agent reads before touching a ticket. Describe what the project is, who it serves, the stack, and any rules of the road.
|
|
74
75
|
3. **Register the MCP server with your agent.** The AI tab generates copy-pasteable snippets for Claude Code, Cursor, Codex, OpenCode, OpenClaw, and other MCP-capable clients — pick your OS + client and paste the command into your agent's MCP config.
|
|
75
76
|
4. **Restart the agent.** On its next boot it discovers the Orbit MCP tools (`board_claim_next`, `board_get_ticket_context`, `board_update_ticket`, etc.).
|
|
@@ -80,7 +81,7 @@ If you initialized with `--example`, ticket #12 (`Try Orbit MCP on this ticket`)
|
|
|
80
81
|
## Vocabulary
|
|
81
82
|
|
|
82
83
|
- **Board** — one repo's planning surface. Each board carries its own `agent_instructions`, lanes, tickets, and memory entries.
|
|
83
|
-
- **Lane** — a column. Lanes are user-defined and freely named/reordered. The seed gives you `Backlog`, `Todo`, `In Progress`, `Review`, `Done`, `Cancelled`;
|
|
84
|
+
- **Lane** — a column. Lanes are user-defined and freely named/reordered. The default seed gives you `Backlog`, `Todo`, `AI Ready`, `In Progress`, `Review`, `Done`, `Cancelled`; `AI Ready`, `In Progress`, and `Review` are anchor lanes the agent flow keys off. `AI Ready` is inserted between `Todo` and `In Progress` when AI is enabled.
|
|
84
85
|
- **Ticket types**:
|
|
85
86
|
- `epic` — index card for a multi-feature initiative. Owns child tickets via `parent_ticket_id`.
|
|
86
87
|
- `feature` — a standalone capability or new behavior.
|
|
@@ -101,7 +102,7 @@ Bun is not used as the Orbit runtime because Orbit depends on Node's built-in `n
|
|
|
101
102
|
|
|
102
103
|
Each repo gets its own board database. Orbit keeps a small central registry that maps repo paths to their board files; the CLI, web app, and MCP server use explicit project roots when provided, then fall back to walking upward to find `.orbit/board.db`.
|
|
103
104
|
|
|
104
|
-
- **Per-repo board** — `.orbit/board.db` lives in the repo it tracks. `orbit init` creates it, drops `SKILL-ORBIT.md` at the repo root, and creates/updates `AGENTS.md` with a terse pointer so agents load the full Orbit protocol only when relevant. Add `--example` to create onboarding cards.
|
|
105
|
+
- **Per-repo board** — `.orbit/board.db` lives in the repo it tracks. `orbit init` creates it, drops `SKILL-ORBIT.md` at the repo root, and creates/updates `AGENTS.md` with a terse pointer so agents load the full Orbit protocol only when relevant. Add `--example` to create onboarding cards, or `--no-ai` to disable AI collaboration for the board.
|
|
105
106
|
- **Central registry** — `registry.db` records each board's path, slug, and last-active timestamp. Host `orbit serve` uses `~/.orbit`; `orbit docker` uses `<repo>/.orbit/docker-data`. Keep one mode unless you intentionally share `DATA_DIR`, re-register boards, or import snapshots.
|
|
106
107
|
- **Project-root discovery** — `orbit serve`, `orbit mcp`, and MCP-attached agents resolve a board from an explicit `--cwd` / `PROJECT_ROOT` first, then fall back to walking up from process cwd to find `.orbit/board.db`. Persistent MCP configs should use an explicit root so the right board attaches even when the agent launches elsewhere.
|
|
107
108
|
- **Two channels for AI** — `AGENTS.md` is the auto-loaded briefing, with an Orbit-managed pointer to `SKILL-ORBIT.md`; the *MCP config* tells the agent runtime how to launch Orbit's MCP server for the right project. The briefing travels with the repo; the MCP registration is per-agent-install.
|
|
@@ -167,7 +168,7 @@ A copy-editable client config lives at [docs/mcp-client.example.json](docs/mcp-c
|
|
|
167
168
|
- **Board**: name from `BOARD_NAME` env var, otherwise inferred from `package.json` `name` or the repo folder name. Slug from `BOARD_SLUG` or derived from the name.
|
|
168
169
|
- **Repo metadata**: `REPO_URL` env var or `git config --get remote.origin.url`; `SYSTEM_PATH` env var or the repo path passed to `orbit init`.
|
|
169
170
|
- **Default branch**: `DEFAULT_BRANCH` env var, otherwise the current git branch.
|
|
170
|
-
- **Lanes (
|
|
171
|
+
- **Lanes (7)**: `Backlog`, `Todo` (default), `AI Ready`, `In Progress`, `Review`, `Done`, `Cancelled`. Pass `--no-ai` to seed without `AI Ready` and leave AI collaboration disabled.
|
|
171
172
|
- **Labels (7)**: `human-only`, `needs-human-input`, `needs-decomposition`, `needs-followup`, `tech-debt`, `security`, `onboarding`.
|
|
172
173
|
- **Onboarding cards (optional)**: pass `--example` to create an epic `#1` plus children `#2`, `#3`, and `#12 — Try Orbit MCP on this ticket`.
|
|
173
174
|
- **Free-form fields**: `project_notes` (Notes tab) and `agent_instructions` (AI tab) get placeholder text you should replace.
|
package/dist/full/version.json
CHANGED
package/docs/MCP_SETUP.md
CHANGED
|
@@ -13,7 +13,7 @@ Orbit generates neutral template paths for Windows, macOS, and Linux. Replace th
|
|
|
13
13
|
|
|
14
14
|
Board data lives under each tracked repo at `.orbit/board.db` (flat file or `.orbit/boards/<slug>/` for extra boards)—not beside the MCP script.
|
|
15
15
|
|
|
16
|
-
After installing Orbit as a package, run **`orbit init`** in (or with `--cwd` pointing at) the repo where you want `.orbit/`, **`AGENTS.md`**, and a copy of **`SKILL-ORBIT.md`** so agents and MCP line up with that tree.
|
|
16
|
+
After installing Orbit as a package, run **`orbit init`** in (or with `--cwd` pointing at) the repo where you want `.orbit/`, **`AGENTS.md`**, and a copy of **`SKILL-ORBIT.md`** so agents and MCP line up with that tree. Orbit enables the AI Ready / In Progress / Review agent lanes by default. Add `--no-ai` if you want AI collaboration disabled, and add `--example` if you want the onboarding example tickets.
|
|
17
17
|
|
|
18
18
|
The generated format changes by app:
|
|
19
19
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@starlove/orbit",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.3",
|
|
4
4
|
"description": "Starscape Orbit — SQLite-backed, agent-first task board (Starscape suite).",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -15,8 +15,6 @@
|
|
|
15
15
|
"build.mjs",
|
|
16
16
|
"docs",
|
|
17
17
|
"dist/full",
|
|
18
|
-
"pnpm-lock.yaml",
|
|
19
|
-
"pnpm-workspace.yaml",
|
|
20
18
|
"public",
|
|
21
19
|
"src",
|
|
22
20
|
"package.json"
|
package/src/cli/orbit.js
CHANGED
|
@@ -23,7 +23,7 @@ Options (init / reset / serve / docker / mcp):
|
|
|
23
23
|
--cwd <dir> Project root (default: process.cwd())
|
|
24
24
|
|
|
25
25
|
Options (init only):
|
|
26
|
-
--ai
|
|
26
|
+
--no-ai Create the board with AI collaboration disabled
|
|
27
27
|
--example Create example onboarding tickets
|
|
28
28
|
--refresh-agents-md Overwrite SKILL-ORBIT.md and refresh Orbit section in AGENTS.md
|
|
29
29
|
|
|
@@ -34,7 +34,8 @@ Options (docker only):
|
|
|
34
34
|
--image <name> Docker image tag (default: starscape-orbit:local)
|
|
35
35
|
--data-dir <dir> Docker registry/export data dir (default: <cwd>/.orbit/docker-data)
|
|
36
36
|
--name <name> Optional Docker container name
|
|
37
|
-
--detach
|
|
37
|
+
-d, --detach Run the container in the background (default)
|
|
38
|
+
--foreground Run attached in the foreground
|
|
38
39
|
--no-build Skip docker build and run an existing image
|
|
39
40
|
--dry-run Print docker commands without running them`);
|
|
40
41
|
}
|
|
@@ -134,29 +135,37 @@ function syncAgentsMd(projectRoot, options) {
|
|
|
134
135
|
console.log(`Appended Orbit instructions to ${agentsPath}`);
|
|
135
136
|
}
|
|
136
137
|
|
|
137
|
-
function
|
|
138
|
+
function enableAiIfEnabled(options, registryRow, core) {
|
|
138
139
|
if (!options.ai || !registryRow) return;
|
|
139
140
|
const { enableAiCollaboration, openConnection } = core;
|
|
140
141
|
const db = openConnection(registryRow.db_path);
|
|
141
|
-
enableAiCollaboration(db, registryRow.id, { stageOnboardingTicket: options.example, actor: "orbit init
|
|
142
|
+
enableAiCollaboration(db, registryRow.id, { stageOnboardingTicket: options.example, actor: "orbit init" });
|
|
142
143
|
const suffix = options.example ? "; example ticket #12 is staged in AI Ready." : ".";
|
|
143
144
|
console.log(`AI collaboration enabled — AI Ready, In Progress, and Review lanes are present${suffix}`);
|
|
144
145
|
}
|
|
145
146
|
|
|
147
|
+
function disableAiIfRequested(options, registryRow, core) {
|
|
148
|
+
if (options.ai || !registryRow) return;
|
|
149
|
+
const { openConnection } = core;
|
|
150
|
+
const db = openConnection(registryRow.db_path);
|
|
151
|
+
db.prepare("UPDATE boards SET ai_enabled = 0, updated_at = ? WHERE id = ?").run(new Date().toISOString(), registryRow.id);
|
|
152
|
+
console.log("AI collaboration disabled for this board.");
|
|
153
|
+
}
|
|
154
|
+
|
|
146
155
|
function parseArgs(argv) {
|
|
147
156
|
const args = {
|
|
148
157
|
command: null,
|
|
149
158
|
cwd: process.env.PROJECT_ROOT ? resolve(process.env.PROJECT_ROOT) : process.cwd(),
|
|
150
159
|
cwdProvided: false,
|
|
151
160
|
refreshAgentsMd: false,
|
|
152
|
-
ai:
|
|
161
|
+
ai: true,
|
|
153
162
|
example: false,
|
|
154
163
|
port: null,
|
|
155
164
|
image: "starscape-orbit:local",
|
|
156
165
|
dataDir: null,
|
|
157
166
|
containerName: null,
|
|
158
167
|
buildImage: true,
|
|
159
|
-
detach:
|
|
168
|
+
detach: true,
|
|
160
169
|
dryRun: false
|
|
161
170
|
};
|
|
162
171
|
const rest = argv.slice(2);
|
|
@@ -168,7 +177,7 @@ function parseArgs(argv) {
|
|
|
168
177
|
for (let i = 1; i < rest.length; i++) {
|
|
169
178
|
const a = rest[i];
|
|
170
179
|
if (a === "--refresh-agents-md") args.refreshAgentsMd = true;
|
|
171
|
-
else if (a === "--ai") args.ai =
|
|
180
|
+
else if (a === "--no-ai") args.ai = false;
|
|
172
181
|
else if (a === "--example") args.example = true;
|
|
173
182
|
else if (a === "--cwd" && rest[i + 1]) {
|
|
174
183
|
args.cwdProvided = true;
|
|
@@ -194,8 +203,10 @@ function parseArgs(argv) {
|
|
|
194
203
|
args.containerName = a.slice("--name=".length);
|
|
195
204
|
} else if (a === "--no-build") {
|
|
196
205
|
args.buildImage = false;
|
|
197
|
-
} else if (a === "--detach") {
|
|
206
|
+
} else if (a === "-d" || a === "--detach") {
|
|
198
207
|
args.detach = true;
|
|
208
|
+
} else if (a === "--foreground") {
|
|
209
|
+
args.detach = false;
|
|
199
210
|
} else if (a === "--dry-run") {
|
|
200
211
|
args.dryRun = true;
|
|
201
212
|
} else {
|
|
@@ -214,7 +225,7 @@ async function runInit(options) {
|
|
|
214
225
|
mkdirSync(DATA_DIR, { recursive: true });
|
|
215
226
|
createRegistrySchema();
|
|
216
227
|
|
|
217
|
-
const result = provisionRepoBoard(projectRoot, { includeExamples: options.example });
|
|
228
|
+
const result = provisionRepoBoard(projectRoot, { enableAi: options.ai, includeExamples: options.example });
|
|
218
229
|
const skillSrc = resolve(ROOT_DIR, "SKILL-ORBIT.md");
|
|
219
230
|
const skillDest = resolve(projectRoot, "SKILL-ORBIT.md");
|
|
220
231
|
|
|
@@ -232,7 +243,8 @@ async function runInit(options) {
|
|
|
232
243
|
syncAgentsMd(projectRoot, options);
|
|
233
244
|
}
|
|
234
245
|
|
|
235
|
-
|
|
246
|
+
enableAiIfEnabled(options, result.registryRow, core);
|
|
247
|
+
disableAiIfRequested(options, result.registryRow, core);
|
|
236
248
|
|
|
237
249
|
if (result.message && result.status === "skipped") {
|
|
238
250
|
console.log(result.message);
|
|
@@ -251,7 +263,7 @@ ${mcpLine}
|
|
|
251
263
|
--- Next: agent prompt (from this repo) ---
|
|
252
264
|
Read AGENTS.md in this repository, then use the Orbit MCP tools. Use board_list, board_set_active, and ticket context tools per docs/AGENT_PROTOCOL.md before claiming or updating work.
|
|
253
265
|
|
|
254
|
-
${options.example ? (options.ai ? "Example ticket #12 is \"Try Orbit MCP on this ticket\" in AI Ready — good first exercise after MCP is connected." : "Example tickets were created.
|
|
266
|
+
${options.example ? (options.ai ? "Example ticket #12 is \"Try Orbit MCP on this ticket\" in AI Ready — good first exercise after MCP is connected." : "Example tickets were created. AI collaboration was disabled with `--no-ai`, so ticket #12 remains in Backlog.") : "No example tickets were created. Rerun on a fresh board with `orbit init --example` if you want the onboarding examples."}
|
|
255
267
|
`);
|
|
256
268
|
|
|
257
269
|
if (result.registryRow) {
|
package/src/core/boards.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { getRegistry, tx } from "./db.js";
|
|
2
|
-
import { boardById } from "./queries.js";
|
|
2
|
+
import { boardById, compactStatePositions } from "./queries.js";
|
|
3
3
|
import {
|
|
4
4
|
computeNewBoardDbPath,
|
|
5
5
|
ensureBoardDbFileAndSchema,
|
|
@@ -33,14 +33,23 @@ export function createBoard(body, ctx) {
|
|
|
33
33
|
const time = now();
|
|
34
34
|
const sniff = buildBoardFromRepo(repoPath);
|
|
35
35
|
|
|
36
|
-
const
|
|
36
|
+
const aiEnabled = body.ai_enabled === undefined ? true : Boolean(body.ai_enabled);
|
|
37
|
+
const defaultStates = body.states || (aiEnabled ? [
|
|
37
38
|
["Backlog", null],
|
|
38
39
|
["Todo", null],
|
|
40
|
+
["AI Ready", "ai_ready"],
|
|
39
41
|
["In Progress", "in_progress"],
|
|
40
42
|
["Review", "review"],
|
|
41
43
|
["Done", "done"],
|
|
42
44
|
["Cancelled", null]
|
|
43
|
-
]
|
|
45
|
+
] : [
|
|
46
|
+
["Backlog", null],
|
|
47
|
+
["Todo", null],
|
|
48
|
+
["In Progress", "in_progress"],
|
|
49
|
+
["Review", "review"],
|
|
50
|
+
["Done", "done"],
|
|
51
|
+
["Cancelled", null]
|
|
52
|
+
]);
|
|
44
53
|
|
|
45
54
|
// Board row + lanes + creation event land atomically in the board DB. The
|
|
46
55
|
// registry insert below is a different database (can't share this
|
|
@@ -49,8 +58,8 @@ export function createBoard(body, ctx) {
|
|
|
49
58
|
tx(db, () => {
|
|
50
59
|
db.prepare(
|
|
51
60
|
`INSERT INTO boards
|
|
52
|
-
(id, slug, name, repo_url, system_path, default_branch, created_at, updated_at)
|
|
53
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`
|
|
61
|
+
(id, slug, name, repo_url, system_path, default_branch, ai_enabled, created_at, updated_at)
|
|
62
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`
|
|
54
63
|
).run(
|
|
55
64
|
boardId,
|
|
56
65
|
slug,
|
|
@@ -58,6 +67,7 @@ export function createBoard(body, ctx) {
|
|
|
58
67
|
body.repo_url || sniff.repoUrl || "",
|
|
59
68
|
repoPath,
|
|
60
69
|
body.default_branch || sniff.defaultBranch || "main",
|
|
70
|
+
aiEnabled ? 1 : 0,
|
|
61
71
|
time,
|
|
62
72
|
time
|
|
63
73
|
);
|
|
@@ -70,6 +80,7 @@ export function createBoard(body, ctx) {
|
|
|
70
80
|
).run(id(), boardId, stateName, position, stateName === "Todo" ? 1 : 0, role || null, time);
|
|
71
81
|
});
|
|
72
82
|
|
|
83
|
+
if (aiEnabled) ensureAgentLanes(db, boardId);
|
|
73
84
|
recordEvent(db, boardId, "board_created", null, actor.name, { board_id: boardId, slug });
|
|
74
85
|
});
|
|
75
86
|
|
|
@@ -95,24 +106,58 @@ const AGENT_LANES = [
|
|
|
95
106
|
{ name: "Review", role: "review" }
|
|
96
107
|
];
|
|
97
108
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
109
|
+
function desiredAgentLanePosition(db, boardId, role) {
|
|
110
|
+
if (role === "ai_ready") {
|
|
111
|
+
const todo = db.prepare("SELECT position FROM states WHERE board_id = ? AND name = 'Todo'").get(boardId);
|
|
112
|
+
if (todo) return Number(todo.position) + 1;
|
|
113
|
+
const inProgress = db.prepare("SELECT position FROM states WHERE board_id = ? AND role = 'in_progress'").get(boardId);
|
|
114
|
+
if (inProgress) return Number(inProgress.position);
|
|
115
|
+
}
|
|
116
|
+
const maxPosition = db
|
|
117
|
+
.prepare("SELECT MAX(position) AS value FROM states WHERE board_id = ?")
|
|
118
|
+
.get(boardId).value;
|
|
119
|
+
return Number(maxPosition ?? -1) + 1;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function openStateSlot(db, boardId, position, exceptStateId = null) {
|
|
123
|
+
db.prepare(
|
|
124
|
+
`UPDATE states
|
|
125
|
+
SET position = position + 1
|
|
126
|
+
WHERE board_id = ?
|
|
127
|
+
AND position >= ?
|
|
128
|
+
AND (? IS NULL OR id != ?)`
|
|
129
|
+
).run(boardId, position, exceptStateId, exceptStateId);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/** Ensure the role-bearing lanes that agent flow depends on exist on the
|
|
133
|
+
* board. AI Ready is kept directly after Todo so claim-next has a predictable
|
|
134
|
+
* handoff column. Idempotent. */
|
|
101
135
|
export function ensureAgentLanes(db, boardId) {
|
|
102
136
|
const time = now();
|
|
103
137
|
for (const lane of AGENT_LANES) {
|
|
104
138
|
const existing = db
|
|
105
|
-
.prepare("SELECT id FROM states WHERE board_id = ? AND role = ?")
|
|
139
|
+
.prepare("SELECT id, position FROM states WHERE board_id = ? AND role = ?")
|
|
106
140
|
.get(boardId, lane.role);
|
|
107
|
-
if (existing)
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
141
|
+
if (existing) {
|
|
142
|
+
if (lane.role === "ai_ready") {
|
|
143
|
+
let desiredPosition = desiredAgentLanePosition(db, boardId, lane.role);
|
|
144
|
+
if (Number(existing.position) !== desiredPosition) {
|
|
145
|
+
db.prepare("UPDATE states SET position = -1 WHERE id = ?").run(existing.id);
|
|
146
|
+
compactStatePositions(db, boardId);
|
|
147
|
+
desiredPosition = desiredAgentLanePosition(db, boardId, lane.role);
|
|
148
|
+
openStateSlot(db, boardId, desiredPosition, existing.id);
|
|
149
|
+
db.prepare("UPDATE states SET position = ? WHERE id = ?").run(desiredPosition, existing.id);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
continue;
|
|
153
|
+
}
|
|
154
|
+
const position = desiredAgentLanePosition(db, boardId, lane.role);
|
|
155
|
+
openStateSlot(db, boardId, position);
|
|
111
156
|
db.prepare(
|
|
112
157
|
`INSERT INTO states
|
|
113
158
|
(id, board_id, name, position, is_default, role, created_at)
|
|
114
159
|
VALUES (?, ?, ?, ?, 0, ?, ?)`
|
|
115
|
-
).run(id(), boardId, lane.name,
|
|
160
|
+
).run(id(), boardId, lane.name, position, lane.role, time);
|
|
116
161
|
}
|
|
117
162
|
}
|
|
118
163
|
|
|
@@ -45,7 +45,10 @@ export function provisionRepoBoard(repoPathRaw, options = {}) {
|
|
|
45
45
|
|
|
46
46
|
const db = openConnection(dbPath);
|
|
47
47
|
createBoardSchema(db);
|
|
48
|
-
const seeded = seedIfEmpty(db, repoRoot, {
|
|
48
|
+
const seeded = seedIfEmpty(db, repoRoot, {
|
|
49
|
+
enableAi: options.enableAi,
|
|
50
|
+
includeExamples: options.includeExamples
|
|
51
|
+
});
|
|
49
52
|
if (!seeded) {
|
|
50
53
|
const boardMeta = readBoardMetaFromSqlite(db);
|
|
51
54
|
if (!boardMeta) {
|
|
@@ -60,7 +63,10 @@ export function provisionRepoBoard(repoPathRaw, options = {}) {
|
|
|
60
63
|
function provisionExistingDbFile(repoRoot, dbPath, options = {}) {
|
|
61
64
|
const db = openConnection(dbPath);
|
|
62
65
|
createBoardSchema(db);
|
|
63
|
-
const seeded = seedIfEmpty(db, repoRoot, {
|
|
66
|
+
const seeded = seedIfEmpty(db, repoRoot, {
|
|
67
|
+
enableAi: options.enableAi,
|
|
68
|
+
includeExamples: options.includeExamples
|
|
69
|
+
});
|
|
64
70
|
if (seeded) {
|
|
65
71
|
return insertAfterSeed(repoRoot, dbPath, db, seeded, "repaired");
|
|
66
72
|
}
|
package/src/core/seed.js
CHANGED
|
@@ -23,8 +23,8 @@ export function seedIfEmpty(db, repoPath, options = {}) {
|
|
|
23
23
|
db.prepare(
|
|
24
24
|
`INSERT INTO boards
|
|
25
25
|
(id, slug, name, repo_url, system_path, default_branch,
|
|
26
|
-
project_notes, agent_instructions, created_at, updated_at)
|
|
27
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
|
|
26
|
+
project_notes, agent_instructions, ai_enabled, created_at, updated_at)
|
|
27
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
|
|
28
28
|
).run(
|
|
29
29
|
boardId,
|
|
30
30
|
seed.boardSlug,
|
|
@@ -34,17 +34,26 @@ export function seedIfEmpty(db, repoPath, options = {}) {
|
|
|
34
34
|
seed.defaultBranch,
|
|
35
35
|
seed.projectNotes,
|
|
36
36
|
seed.agentInstructions,
|
|
37
|
+
options.enableAi === false ? 0 : 1,
|
|
37
38
|
time,
|
|
38
39
|
time
|
|
39
40
|
);
|
|
40
41
|
|
|
41
|
-
const states = [
|
|
42
|
+
const states = options.enableAi === false ? [
|
|
42
43
|
["Backlog", null],
|
|
43
44
|
["Todo", null],
|
|
44
45
|
["In Progress", "in_progress"],
|
|
45
46
|
["Review", "review"],
|
|
46
47
|
["Done", "done"],
|
|
47
48
|
["Cancelled", null]
|
|
49
|
+
] : [
|
|
50
|
+
["Backlog", null],
|
|
51
|
+
["Todo", null],
|
|
52
|
+
["AI Ready", "ai_ready"],
|
|
53
|
+
["In Progress", "in_progress"],
|
|
54
|
+
["Review", "review"],
|
|
55
|
+
["Done", "done"],
|
|
56
|
+
["Cancelled", null]
|
|
48
57
|
];
|
|
49
58
|
const stateIds = new Map();
|
|
50
59
|
states.forEach(([name, role], position) => {
|
package/pnpm-workspace.yaml
DELETED