@starlove/orbit 1.0.3 → 1.0.4

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,6 @@
1
1
  {
2
2
  "name": "@starlove/orbit",
3
- "version": "1.0.3",
3
+ "version": "1.0.4",
4
4
  "edition": "full",
5
- "builtAt": "2026-05-19T05:38:00.174Z"
5
+ "builtAt": "2026-05-19T06:06:41.796Z"
6
6
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@starlove/orbit",
3
- "version": "1.0.3",
3
+ "version": "1.0.4",
4
4
  "description": "Starscape Orbit — SQLite-backed, agent-first task board (Starscape suite).",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -7,7 +7,7 @@ import {
7
7
  createFlyoutInner,
8
8
  createFlyoutBackdrop
9
9
  } from "./dom.js";
10
- import { state, statesForProject, ticketsForProject } from "./state.js";
10
+ import { state, visibleStatesForProject, ticketsForProject } from "./state.js";
11
11
  import { escapeHtml, ticketLabel } from "./format.js";
12
12
  import { api } from "./api.js";
13
13
  import { syncUrlFromState } from "./router.js";
@@ -54,7 +54,7 @@ export function closeCreateFlyout() {
54
54
 
55
55
  /** HTML for the “New” flyout: lane + type + priority on one row, then parent and labels. */
56
56
  function renderCreateForm(preselectedLaneId) {
57
- const states = statesForProject();
57
+ const states = visibleStatesForProject();
58
58
  const defaultState = states.find((item) => item.is_default) || states[0];
59
59
  const effectiveStateId =
60
60
  preselectedLaneId && states.some((s) => s.id === preselectedLaneId)
@@ -4,7 +4,7 @@
4
4
  // owned by ticket-detail.js.
5
5
 
6
6
  import { kanban } from "./dom.js";
7
- import { state, statesForProject, ticketsForProject } from "./state.js";
7
+ import { state, visibleStatesForProject, ticketsForProject } from "./state.js";
8
8
  import {
9
9
  escapeHtml,
10
10
  ticketLabel,
@@ -21,7 +21,7 @@ import { load } from "./app.js";
21
21
  import { unreadCount } from "./unread.js";
22
22
 
23
23
  export function renderBoard() {
24
- const states = statesForProject();
24
+ const states = visibleStatesForProject();
25
25
  const tickets = ticketsForProject();
26
26
  kanban.innerHTML = states.map((column) => renderColumn(column, tickets)).join("");
27
27
 
@@ -144,7 +144,7 @@ function renderProjectAiTab(context) {
144
144
  </span>
145
145
  <span class="orbit-check-label">Enable AI</span>
146
146
  </label>
147
- <p class="description">Provisions the <strong>AI Ready</strong>, <strong>In Progress</strong>, and <strong>Review</strong> lanes if missing, surfaces agent context fields, and exposes the MCP setup snippet. Disabling AI keeps the lanes in place.</p>
147
+ <p class="description">Provisions the <strong>AI Ready</strong>, <strong>In Progress</strong>, and <strong>Review</strong> lanes if missing, surfaces agent context fields, and exposes the MCP setup snippet. Disabling AI hides the AI Ready column but keeps its cards in place.</p>
148
148
  </div>
149
149
  ${aiDivider}
150
150
  ${agentContextSetup}
@@ -387,7 +387,7 @@ function renderDeleteBoardSection(project) {
387
387
  return `
388
388
  <div class="section repository-danger-zone">
389
389
  <h3>Delete Board</h3>
390
- <p class="description">Delete this board from Orbit and remove its local database. Export a snapshot first if you may need this board again.</p>
390
+ <p class="description">Delete this board from Orbit and remove its repo-local Orbit files. Export a snapshot first if you may need this board again.</p>
391
391
  <div class="repository-delete-actions">
392
392
  <button type="button" class="ghost" id="deleteBoardExportFirst">Export first</button>
393
393
  <button type="button" class="ghost danger-button" id="deleteBoardSkipExport">Delete without exporting</button>
@@ -421,7 +421,7 @@ function renderDeleteBoardSection(project) {
421
421
  return `
422
422
  <div class="section repository-danger-zone">
423
423
  <h3>Delete Board</h3>
424
- <p class="description">Remove this board from Orbit and delete its local database. This cannot be undone without an exported snapshot.</p>
424
+ <p class="description">Remove this board from Orbit and delete its repo-local Orbit files. This cannot be undone without an exported snapshot.</p>
425
425
  <button type="button" class="ghost danger-button" id="deleteBoardStart">Delete Board</button>
426
426
  </div>
427
427
  `;
@@ -674,7 +674,7 @@ function bindProjectTabHandlers(context, tab) {
674
674
  });
675
675
  await load();
676
676
  state.detailMode = "settings";
677
- toast.success(enabled ? "AI features enabled — agent lanes provisioned" : "AI features disabled");
677
+ toast.success(enabled ? "AI features enabled — agent lanes provisioned" : "AI features disabled — AI Ready hidden");
678
678
  });
679
679
 
680
680
  }
@@ -804,7 +804,8 @@ function renderProjectEntries(entries = []) {
804
804
  }
805
805
 
806
806
  // In Progress and Review are core kanban lanes — undeletable regardless of
807
- // AI. AI Ready is only locked while AI is enabled; turn AI off to remove it.
807
+ // AI. AI Ready is only locked while AI is enabled; disabling AI hides it from
808
+ // the board without moving its cards.
808
809
  const CORE_LANE_ROLES = new Set(["in_progress", "review"]);
809
810
 
810
811
  /** A lane can't be deleted if it has cards (orphan risk), if it's the default
@@ -831,7 +832,7 @@ function deleteTitle(lane, counts, aiEnabled) {
831
832
  return "Cannot delete — core kanban lane";
832
833
  }
833
834
  if (aiEnabled && lane.role === "ai_ready") {
834
- return "Cannot delete — required by agent flow (disable AI in Settings → AI to remove)";
835
+ return "Cannot delete — required by agent flow while AI is enabled";
835
836
  }
836
837
  return "Delete lane";
837
838
  }
@@ -850,7 +851,7 @@ function renderLaneManager() {
850
851
  .map((lane, index) => {
851
852
  const isAiReady = lane.role === "ai_ready";
852
853
  const nameTitle = isAiReady
853
- ? "AI Ready lane name is locked — toggle from Settings → AI to remove this column"
854
+ ? "AI Ready lane name is locked — disable AI to hide this column"
854
855
  : "";
855
856
  return `
856
857
  <div class="lane-row${isAiReady ? " lane-row--locked" : ""}" data-lane-id="${lane.id}">
@@ -49,6 +49,22 @@ export function statesForProject() {
49
49
  return state.data.states.filter((item) => item.board_id === state.boardId);
50
50
  }
51
51
 
52
+ export function isAiReadyState(item) {
53
+ return item?.role === "ai_ready" || item?.name === "AI Ready";
54
+ }
55
+
56
+ export function visibleStatesForProject() {
57
+ const states = statesForProject();
58
+ if (currentBoard()?.ai_enabled !== 0) return states;
59
+ return states.filter((item) => !isAiReadyState(item));
60
+ }
61
+
62
+ export function selectableStatesForProject(currentStateId = "") {
63
+ const states = statesForProject();
64
+ if (currentBoard()?.ai_enabled !== 0) return states;
65
+ return states.filter((item) => !isAiReadyState(item) || item.id === currentStateId);
66
+ }
67
+
52
68
  export function ticketsForProject() {
53
69
  return state.data.tickets.filter((item) => item.board_id === state.boardId);
54
70
  }
@@ -4,7 +4,7 @@
4
4
  // related cards. Also owns the small render helpers (status history,
5
5
  // comments, detail-card) that are only used inside the drawer.
6
6
 
7
- import { state, ticketsForProject, statesForProject, boardLabelCatalog, currentBoard } from "./state.js";
7
+ import { state, ticketsForProject, selectableStatesForProject, boardLabelCatalog, currentBoard } from "./state.js";
8
8
  import { drawerInner, drawer, $ } from "./dom.js";
9
9
  import {
10
10
  escapeHtml,
@@ -217,7 +217,7 @@ export async function renderDetail() {
217
217
  // user doesn't see it lingering until the next full re-render.
218
218
  markRead(ticket.id, ticket.updated_at);
219
219
  document.querySelector(`.card[data-ticket-id="${CSS.escape(ticket.id)}"] .card-unread-dot`)?.remove();
220
- const states = statesForProject();
220
+ const states = selectableStatesForProject(ticket.state_id);
221
221
 
222
222
  // Ordered by the canonical hierarchy so the dropdown visually reinforces scale:
223
223
  // Epic (biggest) → Feature → Task → Bug.
package/src/cli/orbit.js CHANGED
@@ -5,9 +5,10 @@
5
5
  */
6
6
 
7
7
  import { spawnSync } from "node:child_process";
8
- import { copyFileSync, existsSync, mkdirSync, readFileSync, rmSync, unlinkSync, writeFileSync } from "node:fs";
8
+ import { copyFileSync, existsSync, mkdirSync, rmSync, unlinkSync } from "node:fs";
9
9
  import { dirname, resolve } from "node:path";
10
10
  import { fileURLToPath } from "node:url";
11
+ import { removeOrbitAgentsSection, syncAgentsMd } from "../core/agents-md.js";
11
12
 
12
13
  const PACKAGE_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), "..", "..");
13
14
 
@@ -54,12 +55,9 @@ function formatCommand(executable, args) {
54
55
  return [executable, ...args].map(commandQuote).join(" ");
55
56
  }
56
57
 
57
- const ORBIT_AGENTS_START = "<!-- ORBIT:AGENTS-START -->";
58
- const ORBIT_AGENTS_END = "<!-- ORBIT:AGENTS-END -->";
59
-
60
58
  async function loadCliCore() {
61
59
  const [
62
- { enableAiCollaboration },
60
+ { disableAiCollaboration, enableAiCollaboration },
63
61
  { backupBoardDatabase, backupRegistry },
64
62
  { createRegistrySchema, openConnection },
65
63
  { DATA_DIR, ROOT_DIR },
@@ -83,6 +81,7 @@ async function loadCliCore() {
83
81
  backupRegistry,
84
82
  createRegistrySchema,
85
83
  deleteBoard,
84
+ disableAiCollaboration,
86
85
  enableAiCollaboration,
87
86
  getBoardByRepoPath,
88
87
  normalizePath,
@@ -91,50 +90,6 @@ async function loadCliCore() {
91
90
  };
92
91
  }
93
92
 
94
- function orbitAgentsSection() {
95
- return `${ORBIT_AGENTS_START}
96
- When work mentions Orbit, kanban, board, lane, ticket, card, epic, blocker, claim, AI Ready, implementation fields, planning state, project memory, or handoff: read \`SKILL-ORBIT.md\` first and follow it.
97
- ${ORBIT_AGENTS_END}\n`;
98
- }
99
-
100
- function buildAgentsMd() {
101
- return `# AGENTS.md
102
-
103
- Git is canonical for code. \`SKILL-ORBIT.md\` is canonical for Orbit/kanban/ticket/card workflow.
104
-
105
- ${orbitAgentsSection()}`;
106
- }
107
-
108
- function syncAgentsMd(projectRoot, options) {
109
- const agentsPath = resolve(projectRoot, "AGENTS.md");
110
- const section = orbitAgentsSection();
111
-
112
- if (!existsSync(agentsPath)) {
113
- writeFileSync(agentsPath, buildAgentsMd(), "utf8");
114
- console.log(`Wrote ${agentsPath}`);
115
- return;
116
- }
117
-
118
- const current = readFileSync(agentsPath, "utf8");
119
- const startIndex = current.indexOf(ORBIT_AGENTS_START);
120
- const endIndex = current.indexOf(ORBIT_AGENTS_END);
121
-
122
- if (startIndex !== -1 && endIndex !== -1 && endIndex > startIndex) {
123
- if (!options.refreshAgentsMd) {
124
- console.log("AGENTS.md already includes Orbit instructions — left unchanged (use --refresh-agents-md to replace).");
125
- return;
126
- }
127
- const before = current.slice(0, startIndex).replace(/\s*$/, "\n\n");
128
- const after = current.slice(endIndex + ORBIT_AGENTS_END.length).replace(/^\s*/, "");
129
- writeFileSync(agentsPath, `${before}${section}${after}`, "utf8");
130
- console.log(`Refreshed Orbit instructions inside ${agentsPath}`);
131
- return;
132
- }
133
-
134
- writeFileSync(agentsPath, `${current.replace(/\s*$/, "\n\n")}${section}`, "utf8");
135
- console.log(`Appended Orbit instructions to ${agentsPath}`);
136
- }
137
-
138
93
  function enableAiIfEnabled(options, registryRow, core) {
139
94
  if (!options.ai || !registryRow) return;
140
95
  const { enableAiCollaboration, openConnection } = core;
@@ -146,9 +101,9 @@ function enableAiIfEnabled(options, registryRow, core) {
146
101
 
147
102
  function disableAiIfRequested(options, registryRow, core) {
148
103
  if (options.ai || !registryRow) return;
149
- const { openConnection } = core;
104
+ const { disableAiCollaboration, openConnection } = core;
150
105
  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);
106
+ disableAiCollaboration(db, registryRow.id, { actor: "orbit init" });
152
107
  console.log("AI collaboration disabled for this board.");
153
108
  }
154
109
 
@@ -304,6 +259,12 @@ async function runReset(options) {
304
259
  unlinkSync(skillMd);
305
260
  removed.push(skillMd);
306
261
  }
262
+ const agentsCleanup = removeOrbitAgentsSection(projectRoot);
263
+ if (agentsCleanup.removed) {
264
+ removed.push(`${agentsCleanup.path} Orbit section`);
265
+ } else if (!agentsCleanup.ok) {
266
+ console.warn(`Warning: skipped AGENTS.md cleanup (${agentsCleanup.reason}) in ${agentsCleanup.path}`);
267
+ }
307
268
 
308
269
  if (registryRow) {
309
270
  deleteBoard(registryRow.id);
@@ -447,4 +408,6 @@ async function main() {
447
408
  }
448
409
  }
449
410
 
450
- main();
411
+ await main();
412
+ await new Promise((resolvePromise) => process.stdout.write("", resolvePromise));
413
+ await new Promise((resolvePromise) => process.stderr.write("", resolvePromise));
@@ -0,0 +1,88 @@
1
+ import { existsSync, readFileSync, writeFileSync } from "node:fs";
2
+ import { resolve } from "node:path";
3
+
4
+ export const ORBIT_AGENTS_START = "<!-- ORBIT:AGENTS-START -->";
5
+ export const ORBIT_AGENTS_END = "<!-- ORBIT:AGENTS-END -->";
6
+ export const ORBIT_AGENTS_CANONICAL_LINE =
7
+ "Git is canonical for code. `SKILL-ORBIT.md` is canonical for Orbit/kanban/ticket/card workflow.";
8
+
9
+ export function orbitAgentsSection() {
10
+ return `${ORBIT_AGENTS_START}
11
+ ${ORBIT_AGENTS_CANONICAL_LINE}
12
+
13
+ When work mentions Orbit, kanban, board, lane, ticket, card, epic, blocker, claim, AI Ready, implementation fields, planning state, project memory, or handoff: read \`SKILL-ORBIT.md\` first and follow it.
14
+ ${ORBIT_AGENTS_END}\n`;
15
+ }
16
+
17
+ export function buildAgentsMd() {
18
+ return `# AGENTS.md
19
+
20
+ ${orbitAgentsSection()}`;
21
+ }
22
+
23
+ export function syncAgentsMd(projectRoot, options = {}, logger = console.log) {
24
+ const agentsPath = resolve(projectRoot, "AGENTS.md");
25
+ const section = orbitAgentsSection();
26
+
27
+ if (!existsSync(agentsPath)) {
28
+ writeFileSync(agentsPath, buildAgentsMd(), "utf8");
29
+ logger(`Wrote ${agentsPath}`);
30
+ return;
31
+ }
32
+
33
+ const current = readFileSync(agentsPath, "utf8");
34
+ const startIndex = current.indexOf(ORBIT_AGENTS_START);
35
+ const endIndex = current.indexOf(ORBIT_AGENTS_END);
36
+
37
+ if (startIndex !== -1 && endIndex !== -1 && endIndex > startIndex) {
38
+ if (!options.refreshAgentsMd) {
39
+ logger("AGENTS.md already includes Orbit instructions — left unchanged (use --refresh-agents-md to replace).");
40
+ return;
41
+ }
42
+ const before = current.slice(0, startIndex).replace(/\s*$/, "\n\n");
43
+ const after = current.slice(endIndex + ORBIT_AGENTS_END.length).replace(/^\s*/, "");
44
+ writeFileSync(agentsPath, `${before}${section}${after}`, "utf8");
45
+ logger(`Refreshed Orbit instructions inside ${agentsPath}`);
46
+ return;
47
+ }
48
+
49
+ writeFileSync(agentsPath, `${current.replace(/\s*$/, "\n\n")}${section}`, "utf8");
50
+ logger(`Appended Orbit instructions to ${agentsPath}`);
51
+ }
52
+
53
+ function removeExactLegacyIntro(before) {
54
+ const escaped = ORBIT_AGENTS_CANONICAL_LINE.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
55
+ const legacyIntro = new RegExp(`(?:^|\\n)[ \\t]*${escaped}[ \\t]*(?:\\r?\\n)?[ \\t]*$`);
56
+ return before.replace(legacyIntro, (match) => (match.startsWith("\n") ? "\n" : ""));
57
+ }
58
+
59
+ function joinAfterAgentsRemoval(beforeRaw, afterRaw) {
60
+ const before = removeExactLegacyIntro(beforeRaw).replace(/\s*$/, "");
61
+ const after = afterRaw.replace(/^\s*/, "");
62
+ if (before && after) return `${before}\n\n${after}`;
63
+ if (before) return `${before}\n`;
64
+ return after;
65
+ }
66
+
67
+ export function removeOrbitAgentsSection(projectRoot) {
68
+ const agentsPath = resolve(projectRoot, "AGENTS.md");
69
+ if (!existsSync(agentsPath)) {
70
+ return { ok: true, removed: false, path: agentsPath, reason: "agents_md_missing" };
71
+ }
72
+
73
+ const current = readFileSync(agentsPath, "utf8");
74
+ const startMatches = [...current.matchAll(new RegExp(ORBIT_AGENTS_START, "g"))];
75
+ const endMatches = [...current.matchAll(new RegExp(ORBIT_AGENTS_END, "g"))];
76
+ if (startMatches.length === 0 && endMatches.length === 0) {
77
+ return { ok: true, removed: false, path: agentsPath, reason: "orbit_section_missing" };
78
+ }
79
+ if (startMatches.length !== 1 || endMatches.length !== 1 || endMatches[0].index <= startMatches[0].index) {
80
+ return { ok: false, removed: false, path: agentsPath, reason: "ambiguous_orbit_section" };
81
+ }
82
+
83
+ const startIndex = startMatches[0].index;
84
+ const endIndex = endMatches[0].index + ORBIT_AGENTS_END.length;
85
+ const next = joinAfterAgentsRemoval(current.slice(0, startIndex), current.slice(endIndex));
86
+ if (next !== current) writeFileSync(agentsPath, next, "utf8");
87
+ return { ok: true, removed: next !== current, path: agentsPath };
88
+ }
@@ -188,6 +188,18 @@ export function enableAiCollaboration(db, boardId, options = {}) {
188
188
  return boardById(db, boardId);
189
189
  }
190
190
 
191
+ export function disableAiCollaboration(db, boardId, options = {}) {
192
+ const innerBoard = boardById(db, boardId);
193
+ if (!innerBoard) throw httpError(404, "board_not_found");
194
+
195
+ const time = now();
196
+ return tx(db, () => {
197
+ db.prepare("UPDATE boards SET ai_enabled = 0, updated_at = ? WHERE id = ?").run(time, boardId);
198
+ recordEvent(db, boardId, "ai_disabled", null, options.actor || "system", { board_id: boardId });
199
+ return boardById(db, boardId);
200
+ });
201
+ }
202
+
191
203
  export function updateBoard(boardId, body, ctx) {
192
204
  const { db, board, actor } = ctx;
193
205
  if (boardId !== board.id) throw httpError(404, "board_not_found");
@@ -1,9 +1,10 @@
1
- import { existsSync, rmSync } from "node:fs";
1
+ import { existsSync, rmSync, unlinkSync } from "node:fs";
2
2
  import { basename, dirname, join, relative } from "node:path";
3
+ import { removeOrbitAgentsSection } from "./agents-md.js";
3
4
  import { requireBoardAccess } from "./auth.js";
4
5
  import { backupBoardDatabase, backupRegistry, cancelAutomaticBoardBackup } from "./backups.js";
5
6
  import { closeConnection } from "./db.js";
6
- import { deleteBoard, openBoardDb } from "./registry.js";
7
+ import { deleteBoard, listBoards, openBoardDb } from "./registry.js";
7
8
  import { httpError, normalizePath } from "./util.js";
8
9
 
9
10
  function isInside(parent, child) {
@@ -26,6 +27,24 @@ function boardDbDeleteTargets(boardRow) {
26
27
  return [dbPath, `${dbPath}-wal`, `${dbPath}-shm`, `${dbPath}-journal`];
27
28
  }
28
29
 
30
+ function isLastBoardForRepo(boardRow) {
31
+ const repoRoot = normalizePath(boardRow.repo_path || "");
32
+ return !listBoards().some((row) => row.id !== boardRow.id && normalizePath(row.repo_path || "") === repoRoot);
33
+ }
34
+
35
+ function repoArtifactTargets(boardRow) {
36
+ const repoRoot = normalizePath(boardRow.repo_path || "");
37
+ const dbPath = normalizePath(boardRow.db_path || "");
38
+ const orbitRoot = normalizePath(join(repoRoot, ".orbit"));
39
+ if (!repoRoot || !dbPath || !isInside(orbitRoot, dbPath) || basename(dbPath) !== "board.db") {
40
+ throw httpError(409, "unsafe_board_db_path");
41
+ }
42
+ return {
43
+ orbitRoot,
44
+ skillMd: join(repoRoot, "SKILL-ORBIT.md")
45
+ };
46
+ }
47
+
29
48
  export function deleteRegisteredBoard(boardRow, body, actor) {
30
49
  requireBoardAccess(actor, { slug: boardRow.slug });
31
50
 
@@ -33,14 +52,37 @@ export function deleteRegisteredBoard(boardRow, body, actor) {
33
52
  if (confirmation !== boardRow.slug) throw httpError(400, "board_slug_confirmation_required");
34
53
 
35
54
  const deleteFiles = body?.delete_files !== false;
36
- const targets = deleteFiles ? boardDbDeleteTargets(boardRow) : [];
55
+ const removeRepoArtifacts = deleteFiles && isLastBoardForRepo(boardRow);
56
+ const repoArtifacts = removeRepoArtifacts ? repoArtifactTargets(boardRow) : null;
57
+ const targets = deleteFiles
58
+ ? removeRepoArtifacts
59
+ ? [repoArtifacts.orbitRoot]
60
+ : boardDbDeleteTargets(boardRow)
61
+ : [];
62
+ const artifactResults = [];
37
63
  cancelAutomaticBoardBackup(boardRow.id);
38
64
  if (deleteFiles) backupBoardDatabase(boardRow, openBoardDb(boardRow), "pre-board-delete");
39
65
  backupRegistry("pre-board-delete");
40
66
  closeConnection(boardRow.db_path);
41
67
 
42
68
  for (const target of targets) {
43
- if (existsSync(target)) rmSync(target, { recursive: true, force: true });
69
+ if (existsSync(target)) {
70
+ rmSync(target, { recursive: true, force: true });
71
+ artifactResults.push({ path: target, removed: true });
72
+ }
73
+ }
74
+ if (repoArtifacts?.skillMd && existsSync(repoArtifacts.skillMd)) {
75
+ unlinkSync(repoArtifacts.skillMd);
76
+ artifactResults.push({ path: repoArtifacts.skillMd, removed: true });
77
+ }
78
+ if (removeRepoArtifacts) {
79
+ const agentsCleanup = removeOrbitAgentsSection(boardRow.repo_path);
80
+ artifactResults.push({
81
+ path: agentsCleanup.path,
82
+ removed: agentsCleanup.removed,
83
+ ok: agentsCleanup.ok,
84
+ reason: agentsCleanup.reason
85
+ });
44
86
  }
45
87
 
46
88
  deleteBoard(boardRow.id);
@@ -48,6 +90,8 @@ export function deleteRegisteredBoard(boardRow, body, actor) {
48
90
  ok: true,
49
91
  deleted_board_id: boardRow.id,
50
92
  deleted_board_slug: boardRow.slug,
51
- deleted_files: deleteFiles
93
+ deleted_files: deleteFiles,
94
+ deleted_repo_artifacts: removeRepoArtifacts,
95
+ artifacts: artifactResults
52
96
  };
53
97
  }
package/src/core/seed.js CHANGED
@@ -100,7 +100,7 @@ export function seedIfEmpty(db, repoPath, options = {}) {
100
100
  const firstTicket = insertSeedTicket(db, boardId, 2, {
101
101
  title: "Fill agent context & personal notes",
102
102
  description:
103
- "Open Settings → AI (Agent Instructions) and Settings → Notes, and replace the seed text with real project context plus your own reminders.",
103
+ "Open Settings → AI (Agent Instructions) and Settings → Notes, and add real project context plus your own reminders.",
104
104
  type: "feature",
105
105
  parentTicketId: epicTicket,
106
106
  stateId: stateIds.get("Todo"),
@@ -193,7 +193,6 @@ export function buildBoardFromRepo(projectRoot) {
193
193
  "main";
194
194
  const defaultProjectNotes =
195
195
  "Personal reminders (Settings → Notes: Notes For You). Point yourself at SKILL-ORBIT.md, runbooks, or nudges for your tools.";
196
- const defaultAgentInstructions = `Describe why ${boardName} exists, who it serves, and what success looks like. Add features, stack, constraints, and anything agents should know — this project-level context is provided to agents when they work on tickets.`;
197
196
  return {
198
197
  boardName,
199
198
  boardSlug,
@@ -201,7 +200,7 @@ export function buildBoardFromRepo(projectRoot) {
201
200
  systemPath,
202
201
  defaultBranch,
203
202
  projectNotes: readSeedValue("project_notes") || defaultProjectNotes,
204
- agentInstructions: readSeedValue("agent_instructions") || defaultAgentInstructions
203
+ agentInstructions: readSeedValue("agent_instructions") || ""
205
204
  };
206
205
  }
207
206