@starlove/orbit 1.4.0 → 2.0.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/Dockerfile +10 -8
- package/README.md +146 -164
- package/dist/full/csp.txt +1 -0
- package/dist/full/fonts/JetBrains_Mono/JetBrainsMono-subset.woff2 +0 -0
- package/dist/full/fonts/JetBrains_Mono/README.txt +5 -0
- package/dist/full/fonts/Mona_Sans/MonaSans-subset.woff2 +0 -0
- package/{public/fonts/Open_Sans → dist/full/fonts/Mona_Sans}/OFL.txt +93 -93
- package/dist/full/fonts/Mona_Sans/README.txt +10 -0
- package/{public/fonts/JetBrains_Mono → dist/full/fonts/Newsreader}/OFL.txt +93 -93
- package/dist/full/fonts/Newsreader/README.txt +8 -0
- package/dist/full/fonts/Newsreader/newsreader.woff2 +0 -0
- package/dist/full/index.html +36 -653
- package/dist/full/js/app.BmuponPJ.js +32 -0
- package/dist/full/js/app.BmuponPJ.js.map +1 -0
- package/dist/full/js/chunk.1fJGQGbW.js +2 -0
- package/dist/full/js/chunk.1fJGQGbW.js.map +1 -0
- package/dist/full/js/chunk.5D7JO2zp.js +2 -0
- package/dist/full/js/chunk.5D7JO2zp.js.map +1 -0
- package/dist/full/js/chunk.BrNn_D79.js +2 -0
- package/dist/full/js/chunk.BrNn_D79.js.map +1 -0
- package/dist/full/js/chunk.CokJXIqM.js +2 -0
- package/dist/full/js/chunk.CokJXIqM.js.map +1 -0
- package/dist/full/js/chunk.D_Haqy6R.js +2 -0
- package/dist/full/js/chunk.D_Haqy6R.js.map +1 -0
- package/dist/full/js/chunk.RO0jsH6I.js +15 -0
- package/dist/full/js/chunk.RO0jsH6I.js.map +1 -0
- package/dist/full/js/chunk.dqWOEtBM.js +2 -0
- package/dist/full/js/chunk.dqWOEtBM.js.map +1 -0
- package/dist/full/js/warm.f260bbbc.js +15 -0
- package/dist/full/orbits-favicon.png +0 -0
- package/dist/full/orbits-logo-bg-wide.png +0 -0
- package/dist/full/orbits-logo-bg.png +0 -0
- package/dist/full/orbits-logo.png +0 -0
- package/dist/full/styles.2b154ffe.css +1 -0
- package/dist/full/vendor/starlove-ui/theme-init.js +66 -0
- package/dist/full/version.json +2 -2
- package/docs/AGENT_GUIDE.md +114 -0
- package/docs/UPDATING.md +249 -64
- package/lib/adapters/indexeddb/blobs.d.ts +3 -0
- package/lib/adapters/indexeddb/blobs.js +54 -0
- package/lib/adapters/indexeddb/database.d.ts +60 -0
- package/lib/adapters/indexeddb/database.js +126 -0
- package/lib/adapters/indexeddb/index.d.ts +5 -0
- package/lib/adapters/indexeddb/index.js +3 -0
- package/lib/adapters/indexeddb/locks.d.ts +14 -0
- package/lib/adapters/indexeddb/locks.js +40 -0
- package/lib/adapters/indexeddb/store.d.ts +40 -0
- package/lib/adapters/indexeddb/store.js +424 -0
- package/lib/adapters/indexeddb/tables.d.ts +43 -0
- package/lib/adapters/indexeddb/tables.js +127 -0
- package/lib/adapters/memory/blobs.d.ts +3 -0
- package/lib/adapters/memory/blobs.js +44 -0
- package/lib/adapters/memory/board.d.ts +41 -0
- package/lib/adapters/memory/board.js +521 -0
- package/lib/adapters/memory/index.d.ts +2 -0
- package/lib/adapters/memory/index.js +2 -0
- package/lib/adapters/memory/search.d.ts +6 -0
- package/lib/adapters/memory/search.js +30 -0
- package/lib/adapters/memory/store.d.ts +34 -0
- package/lib/adapters/memory/store.js +266 -0
- package/lib/adapters/sqlite/backups.d.ts +53 -0
- package/lib/adapters/sqlite/backups.js +232 -0
- package/lib/adapters/sqlite/blobs.d.ts +14 -0
- package/lib/adapters/sqlite/blobs.js +109 -0
- package/lib/adapters/sqlite/board.d.ts +77 -0
- package/lib/adapters/sqlite/board.js +328 -0
- package/lib/adapters/sqlite/database.d.ts +67 -0
- package/lib/adapters/sqlite/database.js +285 -0
- package/lib/adapters/sqlite/index.d.ts +3 -0
- package/lib/adapters/sqlite/index.js +2 -0
- package/lib/adapters/sqlite/lock.d.ts +11 -0
- package/lib/adapters/sqlite/lock.js +34 -0
- package/lib/adapters/sqlite/records.d.ts +54 -0
- package/lib/adapters/sqlite/records.js +546 -0
- package/lib/adapters/sqlite/schema.d.ts +54 -0
- package/lib/adapters/sqlite/schema.js +276 -0
- package/lib/adapters/sqlite/store.d.ts +39 -0
- package/lib/adapters/sqlite/store.js +385 -0
- package/lib/browser/in-page.d.ts +31 -0
- package/lib/browser/in-page.js +46 -0
- package/lib/browser/old-preview.d.ts +13 -0
- package/lib/browser/old-preview.js +80 -0
- package/lib/browser/preview-board.d.ts +8 -0
- package/lib/browser/preview-board.js +69 -0
- package/lib/cli/main.d.ts +7 -0
- package/lib/cli/main.js +343 -0
- package/lib/cli/orbit.d.ts +8 -0
- package/lib/cli/orbit.js +25 -0
- package/lib/client/http.d.ts +38 -0
- package/lib/client/http.js +334 -0
- package/lib/client/local.d.ts +26 -0
- package/lib/client/local.js +190 -0
- package/lib/client/object-url.d.ts +8 -0
- package/lib/client/object-url.js +21 -0
- package/lib/client/types.d.ts +268 -0
- package/lib/client/types.js +8 -0
- package/lib/commands/agent-tools.d.ts +31 -0
- package/lib/commands/agent-tools.js +100 -0
- package/lib/commands/index.d.ts +56 -0
- package/lib/commands/index.js +298 -0
- package/lib/commands/parse.d.ts +73 -0
- package/lib/commands/parse.js +231 -0
- package/lib/commands/present.d.ts +134 -0
- package/lib/commands/present.js +198 -0
- package/lib/commands/session.d.ts +85 -0
- package/lib/commands/session.js +138 -0
- package/lib/commands/verbs.d.ts +35 -0
- package/lib/commands/verbs.js +842 -0
- package/lib/core/actors.d.ts +18 -0
- package/lib/core/actors.js +45 -0
- package/lib/core/agent.d.ts +128 -0
- package/lib/core/agent.js +323 -0
- package/lib/core/boards.d.ts +113 -0
- package/lib/core/boards.js +288 -0
- package/lib/core/bootstrap.d.ts +47 -0
- package/lib/core/bootstrap.js +100 -0
- package/lib/core/bytes.d.ts +10 -0
- package/lib/core/bytes.js +52 -0
- package/lib/core/card-paths.d.ts +25 -0
- package/lib/core/card-paths.js +44 -0
- package/lib/core/context.d.ts +60 -0
- package/lib/core/context.js +42 -0
- package/lib/core/cross-board.d.ts +81 -0
- package/lib/core/cross-board.js +228 -0
- package/lib/core/describe.d.ts +21 -0
- package/lib/core/describe.js +81 -0
- package/lib/core/errors.d.ts +159 -0
- package/lib/core/errors.js +160 -0
- package/lib/core/files.d.ts +79 -0
- package/lib/core/files.js +262 -0
- package/lib/core/history.d.ts +20 -0
- package/lib/core/history.js +53 -0
- package/lib/core/image-types.d.ts +16 -0
- package/lib/core/image-types.js +36 -0
- package/lib/core/model.d.ts +263 -0
- package/lib/core/model.js +34 -0
- package/lib/core/nebula-link.d.ts +14 -0
- package/lib/core/nebula-link.js +39 -0
- package/lib/core/relations.d.ts +58 -0
- package/lib/core/relations.js +112 -0
- package/lib/core/search.d.ts +38 -0
- package/lib/core/search.js +92 -0
- package/lib/core/sessions.d.ts +44 -0
- package/lib/core/sessions.js +158 -0
- package/lib/core/snapshot-schema.d.ts +295 -0
- package/lib/core/snapshot-schema.js +82 -0
- package/lib/core/snapshots.d.ts +75 -0
- package/lib/core/snapshots.js +409 -0
- package/lib/core/states.d.ts +25 -0
- package/lib/core/states.js +126 -0
- package/lib/core/text.d.ts +40 -0
- package/lib/core/text.js +113 -0
- package/lib/core/tickets.d.ts +69 -0
- package/lib/core/tickets.js +311 -0
- package/lib/core/trello.d.ts +17 -0
- package/lib/core/trello.js +269 -0
- package/lib/core/verdicts.d.ts +28 -0
- package/lib/core/verdicts.js +92 -0
- package/lib/core/views.d.ts +95 -0
- package/lib/core/views.js +186 -0
- package/lib/http/app.d.ts +46 -0
- package/lib/http/app.js +491 -0
- package/lib/mcp-server.d.ts +10 -0
- package/lib/mcp-server.js +120 -0
- package/lib/node/assets.d.ts +7 -0
- package/lib/node/assets.js +68 -0
- package/lib/node/data-folder.d.ts +21 -0
- package/lib/node/data-folder.js +34 -0
- package/lib/node/env.d.ts +38 -0
- package/lib/node/env.js +49 -0
- package/lib/node/host-check.d.ts +24 -0
- package/lib/node/host-check.js +53 -0
- package/lib/node/host-services.d.ts +9 -0
- package/lib/node/host-services.js +150 -0
- package/lib/node/local.d.ts +22 -0
- package/lib/node/local.js +61 -0
- package/lib/node/migrate.d.ts +53 -0
- package/lib/node/migrate.js +635 -0
- package/lib/node/package.d.ts +7 -0
- package/lib/node/package.js +14 -0
- package/lib/node/repo-board.d.ts +49 -0
- package/lib/node/repo-board.js +245 -0
- package/lib/node/serve.d.ts +8 -0
- package/lib/node/serve.js +106 -0
- package/lib/orbit.d.ts +47 -0
- package/lib/orbit.js +44 -0
- package/lib/quiet-sqlite-warning.d.ts +1 -0
- package/lib/quiet-sqlite-warning.js +18 -0
- package/lib/store/blob-path.d.ts +6 -0
- package/lib/store/blob-path.js +12 -0
- package/lib/store/conformance.d.ts +52 -0
- package/lib/store/conformance.js +1449 -0
- package/lib/store/search-words.d.ts +10 -0
- package/lib/store/search-words.js +19 -0
- package/lib/store/types.d.ts +452 -0
- package/lib/store/types.js +34 -0
- package/llms.txt +63 -0
- package/package.json +109 -25
- package/src/cli/orbit.js +11 -658
- package/src/mcp-server.js +16 -876
- package/SKILL-ORBIT.md +0 -151
- package/build.mjs +0 -210
- package/dist/full/js/app.62905fcb.js +0 -952
- package/dist/full/js/app.62905fcb.js.map +0 -7
- package/dist/full/styles.4e60d824.css +0 -1
- package/docs/AGENT_PROTOCOL.md +0 -424
- package/docs/MCP_SETUP.md +0 -54
- package/docs/ORBIT_DISPATCH.md +0 -280
- package/docs/SS-Orbit-Dark.png +0 -0
- package/docs/SS-Orbit-Light.png +0 -0
- package/docs/mcp-client.example.json +0 -13
- package/docs/setup/orbit-init-wizard.md +0 -174
- package/public/apple-touch-icon.png +0 -0
- package/public/cursors/live_arrow.cur +0 -0
- package/public/cursors/live_link.cur +0 -0
- package/public/cursors/live_move.cur +0 -0
- package/public/cursors/live_select.cur +0 -0
- package/public/cursors/live_unavail.cur +0 -0
- package/public/favicon-32x32.png +0 -0
- package/public/fonts/Chakra_Petch/ChakraPetch-Bold.ttf +0 -0
- package/public/fonts/Chakra_Petch/ChakraPetch-BoldItalic.ttf +0 -0
- package/public/fonts/Chakra_Petch/ChakraPetch-Italic.ttf +0 -0
- package/public/fonts/Chakra_Petch/ChakraPetch-Light.ttf +0 -0
- package/public/fonts/Chakra_Petch/ChakraPetch-LightItalic.ttf +0 -0
- package/public/fonts/Chakra_Petch/ChakraPetch-Medium.ttf +0 -0
- package/public/fonts/Chakra_Petch/ChakraPetch-MediumItalic.ttf +0 -0
- package/public/fonts/Chakra_Petch/ChakraPetch-Regular.ttf +0 -0
- package/public/fonts/Chakra_Petch/ChakraPetch-SemiBold.ttf +0 -0
- package/public/fonts/Chakra_Petch/ChakraPetch-SemiBoldItalic.ttf +0 -0
- package/public/fonts/Chakra_Petch/OFL.txt +0 -93
- package/public/fonts/Inter/Inter-Italic-VariableFont_opsz,wght.ttf +0 -0
- package/public/fonts/Inter/Inter-VariableFont_opsz,wght.ttf +0 -0
- package/public/fonts/Inter/OFL.txt +0 -93
- package/public/fonts/Inter/README.txt +0 -118
- package/public/fonts/JetBrains_Mono/JetBrainsMono-Italic-VariableFont_wght.ttf +0 -0
- package/public/fonts/JetBrains_Mono/JetBrainsMono-VariableFont_wght.ttf +0 -0
- package/public/fonts/JetBrains_Mono/README.txt +0 -79
- package/public/fonts/OpenRing-Regular.ttf +0 -0
- package/public/fonts/Open_Sans/OpenSans-Italic-VariableFont_wdth,wght.ttf +0 -0
- package/public/fonts/Open_Sans/OpenSans-VariableFont_wdth,wght.ttf +0 -0
- package/public/fonts/Open_Sans/README.txt +0 -100
- package/public/fonts/Orbitron/OFL.txt +0 -93
- package/public/fonts/Orbitron/Orbitron-VariableFont_wght.ttf +0 -0
- package/public/fonts/Orbitron/README.txt +0 -68
- package/public/fonts/Rajdhani/OFL.txt +0 -93
- package/public/fonts/Rajdhani/Rajdhani-Bold.ttf +0 -0
- package/public/fonts/Rajdhani/Rajdhani-Light.ttf +0 -0
- package/public/fonts/Rajdhani/Rajdhani-Medium.ttf +0 -0
- package/public/fonts/Rajdhani/Rajdhani-Regular.ttf +0 -0
- package/public/fonts/Rajdhani/Rajdhani-SemiBold.ttf +0 -0
- package/public/fonts/runescape-uf.ttf +0 -0
- package/public/index.html +0 -769
- package/public/js/actor-labels.js +0 -65
- package/public/js/api.js +0 -39
- package/public/js/app.js +0 -45
- package/public/js/artifact-drawer.js +0 -79
- package/public/js/board-export.js +0 -161
- package/public/js/board-menu.js +0 -374
- package/public/js/card-actions.js +0 -545
- package/public/js/config.js +0 -20
- package/public/js/create-card.js +0 -190
- package/public/js/dom.js +0 -19
- package/public/js/drawer.js +0 -273
- package/public/js/font-preference.js +0 -229
- package/public/js/format.js +0 -433
- package/public/js/help-menu.js +0 -31
- package/public/js/icons.js +0 -16
- package/public/js/kanban.js +0 -694
- package/public/js/local-backend.js +0 -1015
- package/public/js/main.js +0 -161
- package/public/js/modal.js +0 -33
- package/public/js/motion-preference.js +0 -64
- package/public/js/panel.js +0 -58
- package/public/js/router.js +0 -134
- package/public/js/search.js +0 -56
- package/public/js/settings-tabs.js +0 -7
- package/public/js/settings.js +0 -1634
- package/public/js/sse.js +0 -159
- package/public/js/starscape.js +0 -332
- package/public/js/state.js +0 -129
- package/public/js/text-editing.js +0 -128
- package/public/js/theme-preference.js +0 -95
- package/public/js/ticket-detail.js +0 -1516
- package/public/js/ticket-typeahead.js +0 -49
- package/public/js/toast.js +0 -89
- package/public/js/topbar-notes.js +0 -155
- package/public/js/transport.js +0 -22
- package/public/js/typeahead.js +0 -206
- package/public/js/unread.js +0 -121
- package/public/js/update-check.js +0 -58
- package/public/js/url-routes.js +0 -94
- package/public/orbits-favicon.png +0 -0
- package/public/orbits-logo-bg-wide.png +0 -0
- package/public/orbits-logo-bg.png +0 -0
- package/public/orbits-logo.png +0 -0
- package/public/styles.css +0 -7709
- package/src/core/agent.js +0 -387
- package/src/core/agents-md.js +0 -109
- package/src/core/artifacts.js +0 -149
- package/src/core/attachments.js +0 -238
- package/src/core/auth.js +0 -85
- package/src/core/backups.js +0 -142
- package/src/core/board.js +0 -148
- package/src/core/boards.js +0 -409
- package/src/core/bootstrap.js +0 -170
- package/src/core/css-bundle.js +0 -30
- package/src/core/db.js +0 -315
- package/src/core/delete-board.js +0 -163
- package/src/core/dispatch.js +0 -1007
- package/src/core/events.js +0 -314
- package/src/core/http.js +0 -115
- package/src/core/migrate.js +0 -198
- package/src/core/paths.js +0 -55
- package/src/core/provision-repo-board.js +0 -119
- package/src/core/queries.js +0 -246
- package/src/core/registry.js +0 -211
- package/src/core/relations.js +0 -163
- package/src/core/review-verdicts.js +0 -138
- package/src/core/router.js +0 -556
- package/src/core/search.js +0 -183
- package/src/core/seed.js +0 -234
- package/src/core/snapshots.js +0 -692
- package/src/core/states.js +0 -167
- package/src/core/system-picker.js +0 -109
- package/src/core/tickets.js +0 -400
- package/src/core/util.js +0 -106
- package/src/core/version-check.js +0 -154
- package/src/mcp/orbit-client.js +0 -287
- package/src/server.js +0 -115
package/src/mcp-server.js
CHANGED
|
@@ -1,876 +1,16 @@
|
|
|
1
|
-
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
createComment,
|
|
18
|
-
createTicket,
|
|
19
|
-
deleteTicket,
|
|
20
|
-
exportBoard,
|
|
21
|
-
getBoardContext,
|
|
22
|
-
getAgentDispatchPacket,
|
|
23
|
-
getContextPack,
|
|
24
|
-
readComments,
|
|
25
|
-
readTicket,
|
|
26
|
-
getTicketBlockers,
|
|
27
|
-
getTicketRelations,
|
|
28
|
-
localAgentActor,
|
|
29
|
-
restoreTicket,
|
|
30
|
-
searchTickets,
|
|
31
|
-
updateBoard,
|
|
32
|
-
updateTicket
|
|
33
|
-
} from "./core/board.js";
|
|
34
|
-
import { closeAllConnections, createBoardSchema, openConnection } from "./core/db.js";
|
|
35
|
-
import {
|
|
36
|
-
getBoardByRegistryId,
|
|
37
|
-
getBoardBySlug,
|
|
38
|
-
getBoardByRepoPath,
|
|
39
|
-
insertBoard,
|
|
40
|
-
listBoards,
|
|
41
|
-
openBoardDb,
|
|
42
|
-
syncRegistryFromBoardDb,
|
|
43
|
-
touchBoardActive
|
|
44
|
-
} from "./core/registry.js";
|
|
45
|
-
import { scheduleAutomaticBoardBackup } from "./core/backups.js";
|
|
46
|
-
import { provisionRepoBoard } from "./core/provision-repo-board.js";
|
|
47
|
-
import { seedIfEmpty } from "./core/seed.js";
|
|
48
|
-
import { now, normalizePath } from "./core/util.js";
|
|
49
|
-
|
|
50
|
-
const SERVER_INFO = {
|
|
51
|
-
name: "minimal-agent-board",
|
|
52
|
-
version: "0.1.0"
|
|
53
|
-
};
|
|
54
|
-
|
|
55
|
-
const orbitClient = await createOrbitClient();
|
|
56
|
-
// Per-process session board. Set on init by walking up from cwd, mutable via
|
|
57
|
-
// `board_set_active`. NEVER read by anything outside this file — every
|
|
58
|
-
// downstream call receives an explicit ctx.
|
|
59
|
-
let sessionBoardRow = null;
|
|
60
|
-
|
|
61
|
-
function setSessionBoard(row) {
|
|
62
|
-
sessionBoardRow = row;
|
|
63
|
-
if (row) touchBoardActive(row.id);
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
function getSessionBoard() {
|
|
67
|
-
return sessionBoardRow;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* Walk up from `startDir` toward the filesystem root, returning the first
|
|
72
|
-
* directory that satisfies `predicate`. Returns null if none found.
|
|
73
|
-
*/
|
|
74
|
-
function walkUp(startDir, predicate) {
|
|
75
|
-
let dir = startDir;
|
|
76
|
-
while (true) {
|
|
77
|
-
if (predicate(dir)) return dir;
|
|
78
|
-
const parent = dirname(dir);
|
|
79
|
-
if (parent === dir) return null;
|
|
80
|
-
dir = parent;
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
/**
|
|
85
|
-
* Initialise the MCP session board. Resolution order:
|
|
86
|
-
* 1. Walk up from PROJECT_ROOT (cwd unless `orbit mcp --cwd` or env overrides it)
|
|
87
|
-
* for an existing `.orbit/board.db` — finds the board even when the agent
|
|
88
|
-
* is launched from a subdirectory or MCP config uses an absolute command.
|
|
89
|
-
* 2. Walk up from PROJECT_ROOT for a `.git/` directory — auto-creates a board at
|
|
90
|
-
* the git root on first launch.
|
|
91
|
-
* 3. Fall back to PROJECT_ROOT itself — supports non-git projects and global installs.
|
|
92
|
-
*/
|
|
93
|
-
function initMcpSessionBoard() {
|
|
94
|
-
const start = normalizePath(process.env.PROJECT_ROOT ? resolve(process.env.PROJECT_ROOT) : process.cwd());
|
|
95
|
-
|
|
96
|
-
// 1. Prefer the registry row for PROJECT_ROOT (or one of its ancestors). New
|
|
97
|
-
// Orbit boards live in central DATA_DIR storage, so there may be no in-repo
|
|
98
|
-
// .orbit/board.db to discover.
|
|
99
|
-
const registeredRoot = walkUp(start, (dir) => Boolean(getBoardByRepoPath(normalizePath(dir))));
|
|
100
|
-
if (registeredRoot) {
|
|
101
|
-
setSessionBoard(getBoardByRepoPath(normalizePath(registeredRoot)));
|
|
102
|
-
return;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
// 2. Walk up for an existing legacy board db.
|
|
106
|
-
const boardRoot = walkUp(start, (dir) => existsSync(join(dir, ".orbit", "board.db")));
|
|
107
|
-
if (boardRoot) {
|
|
108
|
-
const root = normalizePath(boardRoot);
|
|
109
|
-
const existing = getBoardByRepoPath(root);
|
|
110
|
-
if (existing) { setSessionBoard(existing); return; }
|
|
111
|
-
// db exists on disk but isn't in the registry yet — register it.
|
|
112
|
-
const dbPath = join(boardRoot, ".orbit", "board.db");
|
|
113
|
-
const db = openConnection(dbPath);
|
|
114
|
-
createBoardSchema(db);
|
|
115
|
-
const seeded = seedIfEmpty(db, root);
|
|
116
|
-
if (seeded) {
|
|
117
|
-
const t = now();
|
|
118
|
-
insertBoard({ id: seeded.id, slug: seeded.slug, name: seeded.name, repo_path: root, db_path: dbPath, repo_url: seeded.repo_url || "", default_branch: seeded.default_branch || "main", last_active_at: t, created_at: t, updated_at: t });
|
|
119
|
-
}
|
|
120
|
-
const fresh = getBoardByRepoPath(root);
|
|
121
|
-
if (fresh) { syncRegistryFromBoardDb(fresh, db); setSessionBoard(fresh); }
|
|
122
|
-
return;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
// 3. Walk up for a git root; fall back to cwd for non-git projects.
|
|
126
|
-
const gitRoot = walkUp(start, (dir) => existsSync(join(dir, ".git")));
|
|
127
|
-
const result = provisionRepoBoard(normalizePath(gitRoot || start));
|
|
128
|
-
if (result.registryRow) setSessionBoard(result.registryRow);
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
function createBoardAtRoot(root) {
|
|
132
|
-
const dbPath = join(root, ".orbit", "board.db");
|
|
133
|
-
mkdirSync(dirname(dbPath), { recursive: true });
|
|
134
|
-
const db = openConnection(dbPath);
|
|
135
|
-
createBoardSchema(db);
|
|
136
|
-
const seeded = seedIfEmpty(db, root);
|
|
137
|
-
if (seeded) {
|
|
138
|
-
const t = now();
|
|
139
|
-
insertBoard({ id: seeded.id, slug: seeded.slug, name: seeded.name, repo_path: root, db_path: dbPath, repo_url: seeded.repo_url || "", default_branch: seeded.default_branch || "main", last_active_at: t, created_at: t, updated_at: t });
|
|
140
|
-
}
|
|
141
|
-
const fresh = getBoardByRepoPath(root);
|
|
142
|
-
if (fresh) { syncRegistryFromBoardDb(fresh, db); setSessionBoard(fresh); }
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
if (orbitClient.mode !== "remote") {
|
|
146
|
-
initMcpSessionBoard();
|
|
147
|
-
closeAllConnections();
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
/** Build a ctx for the session board (or a board the tool argument selected). */
|
|
151
|
-
function ctxFor(boardRow, actor) {
|
|
152
|
-
if (!boardRow) throw rpcError(-32602, "No board in session; pass board_id or board_slug, or call board_set_active first.");
|
|
153
|
-
return { actor, board: boardRow, db: openBoardDb(boardRow) };
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
function sessionCtx(actor) {
|
|
157
|
-
return ctxFor(getSessionBoard(), actor);
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
function withAutomaticBackup(ctx, result) {
|
|
161
|
-
scheduleAutomaticBoardBackup(ctx.board, ctx.db);
|
|
162
|
-
return result;
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
/** Resolve a registry row from explicit args (no session fallback). */
|
|
166
|
-
function resolveExplicitBoard(args) {
|
|
167
|
-
if (args.board_id) {
|
|
168
|
-
const row = getBoardByRegistryId(args.board_id);
|
|
169
|
-
if (!row) throw rpcError(-32004, "Board id not found.");
|
|
170
|
-
return row;
|
|
171
|
-
}
|
|
172
|
-
if (args.board_slug || args.board) {
|
|
173
|
-
const row = getBoardBySlug(args.board_slug || args.board);
|
|
174
|
-
if (!row) throw rpcError(-32004, "Board slug not found.");
|
|
175
|
-
return row;
|
|
176
|
-
}
|
|
177
|
-
return null;
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
/** Resolve a board for a tool that accepts board_id/board_slug AND can fall
|
|
181
|
-
* back to the session board. */
|
|
182
|
-
function resolveBoardOrSession(args) {
|
|
183
|
-
return resolveExplicitBoard(args) || getSessionBoard();
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
const TOOL_DEFS = [
|
|
187
|
-
{
|
|
188
|
-
name: "board_context",
|
|
189
|
-
description:
|
|
190
|
-
"Board/project context pack: agent-safe board metadata, agent_instructions, journal entries (board_entries), and deployment paths. Human-only project_notes are intentionally excluded. Complements the lean board_get_ticket_context. Resolves board from board_id / board_slug / board first; the MCP session board is only a fallback convenience. Set include_struck true to include struck journal rows (default false). Same payload as GET /api/boards/:id/context.",
|
|
191
|
-
inputSchema: {
|
|
192
|
-
type: "object",
|
|
193
|
-
properties: {
|
|
194
|
-
board_id: { type: "string" },
|
|
195
|
-
board_slug: { type: "string" },
|
|
196
|
-
board: { type: "string" },
|
|
197
|
-
include_struck: { type: "boolean", description: "When true, journal entries include struck items." }
|
|
198
|
-
},
|
|
199
|
-
additionalProperties: false
|
|
200
|
-
},
|
|
201
|
-
handler: (args) => orbitClient.boardContext(args)
|
|
202
|
-
},
|
|
203
|
-
{
|
|
204
|
-
name: "board_list",
|
|
205
|
-
description:
|
|
206
|
-
"Tiny registry index: id, slug, name, repo_path per board. Does not open board databases or load tickets. Pair with board_set_active and board_context / ticket tools; the web UI loads full rows via GET /api/bootstrap.",
|
|
207
|
-
inputSchema: {
|
|
208
|
-
type: "object",
|
|
209
|
-
properties: {},
|
|
210
|
-
additionalProperties: false
|
|
211
|
-
},
|
|
212
|
-
handler: () => orbitClient.boardList()
|
|
213
|
-
},
|
|
214
|
-
{
|
|
215
|
-
name: "board_set_active",
|
|
216
|
-
description:
|
|
217
|
-
"Switch the MCP session fallback to another board by slug (updates last_active_at for multi-board repos). Tools that accept board_id / board_slug / board use those explicit selectors first; this session board is only the convenience fallback when selectors are omitted.",
|
|
218
|
-
inputSchema: {
|
|
219
|
-
type: "object",
|
|
220
|
-
properties: {
|
|
221
|
-
slug: { type: "string" }
|
|
222
|
-
},
|
|
223
|
-
required: ["slug"],
|
|
224
|
-
additionalProperties: false
|
|
225
|
-
},
|
|
226
|
-
handler: (args) => orbitClient.boardSetActive(args)
|
|
227
|
-
},
|
|
228
|
-
{
|
|
229
|
-
name: "board_claim_next",
|
|
230
|
-
description:
|
|
231
|
-
"Claim the next schedulable AI-ready ticket on a specific board. Resolves board from board_id / board_slug / board first; the MCP session board is only a fallback convenience when selectors are omitted, and omitted selectors never scan other boards.",
|
|
232
|
-
inputSchema: {
|
|
233
|
-
type: "object",
|
|
234
|
-
properties: {
|
|
235
|
-
board: { type: "string" },
|
|
236
|
-
board_slug: { type: "string" },
|
|
237
|
-
board_id: { type: "string" },
|
|
238
|
-
type: { type: "string" },
|
|
239
|
-
include_epics: { type: "boolean" }
|
|
240
|
-
},
|
|
241
|
-
additionalProperties: false
|
|
242
|
-
},
|
|
243
|
-
handler: (args) => orbitClient.claimNext(args)
|
|
244
|
-
},
|
|
245
|
-
{
|
|
246
|
-
name: "board_get_ticket_context",
|
|
247
|
-
description:
|
|
248
|
-
"Return the default lean context pack for a ticket: ticket fields, board identity only, relations, blockers, parent/children, and related ticket summaries. Resolves board from board_id / board_slug / board first; the MCP session board is only a fallback convenience. Project manual/journal and comments are intentionally omitted; use board_context for board manual/journal and board_read_comments for comment threads.",
|
|
249
|
-
inputSchema: {
|
|
250
|
-
type: "object",
|
|
251
|
-
properties: {
|
|
252
|
-
ticket_id: { type: "string" },
|
|
253
|
-
board_id: { type: "string" },
|
|
254
|
-
board_slug: { type: "string" },
|
|
255
|
-
board: { type: "string" },
|
|
256
|
-
depth: { type: "integer", minimum: 1, maximum: 3 },
|
|
257
|
-
max_chars_per_field: { type: "integer", minimum: 1 },
|
|
258
|
-
include_parent_full: { type: "boolean" },
|
|
259
|
-
include_related_full: { type: "boolean" }
|
|
260
|
-
},
|
|
261
|
-
required: ["ticket_id"],
|
|
262
|
-
additionalProperties: false
|
|
263
|
-
},
|
|
264
|
-
handler: (args) => orbitClient.getTicketContext(args)
|
|
265
|
-
},
|
|
266
|
-
{
|
|
267
|
-
name: "board_get_ticket_context_full",
|
|
268
|
-
description:
|
|
269
|
-
"Return the explicit heavy ticket context pack, preserving the historical board_get_ticket_context shape: ticket, full board row, board_manual/journal, relations, blockers, parent/children, and related tickets. Resolves board from board_id / board_slug / board first; the MCP session board is only a fallback convenience. Comments are still intentionally omitted; use board_read_comments for comment threads.",
|
|
270
|
-
inputSchema: {
|
|
271
|
-
type: "object",
|
|
272
|
-
properties: {
|
|
273
|
-
ticket_id: { type: "string" },
|
|
274
|
-
board_id: { type: "string" },
|
|
275
|
-
board_slug: { type: "string" },
|
|
276
|
-
board: { type: "string" },
|
|
277
|
-
depth: { type: "integer", minimum: 1, maximum: 3 },
|
|
278
|
-
max_chars_per_field: { type: "integer", minimum: 1 },
|
|
279
|
-
include_parent_full: { type: "boolean" },
|
|
280
|
-
include_related_full: { type: "boolean" }
|
|
281
|
-
},
|
|
282
|
-
required: ["ticket_id"],
|
|
283
|
-
additionalProperties: false
|
|
284
|
-
},
|
|
285
|
-
handler: (args) => orbitClient.getTicketContextFull(args)
|
|
286
|
-
},
|
|
287
|
-
{
|
|
288
|
-
name: "board_get_agent_dispatch_packet",
|
|
289
|
-
description:
|
|
290
|
-
"Return a lean agent dispatch packet for one ticket: board identity/instructions, capped description/acceptance, human-visible card fields and linked artifact paths, blockers, shallow parent, relevant workflow state IDs, repo path/default branch, recent capped comments, and label names. Resolves board from board_id / board_slug / board first; the MCP session board is only a fallback convenience. Full parent/related bodies and full comments are intentionally omitted.",
|
|
291
|
-
inputSchema: {
|
|
292
|
-
type: "object",
|
|
293
|
-
properties: {
|
|
294
|
-
ticket_id: { type: "string" },
|
|
295
|
-
board_id: { type: "string" },
|
|
296
|
-
board_slug: { type: "string" },
|
|
297
|
-
board: { type: "string" },
|
|
298
|
-
max_chars_per_field: { type: "integer", minimum: 1 },
|
|
299
|
-
comment_limit: { type: "integer", minimum: 0, maximum: 20 }
|
|
300
|
-
},
|
|
301
|
-
required: ["ticket_id"],
|
|
302
|
-
additionalProperties: false
|
|
303
|
-
},
|
|
304
|
-
handler: (args) => orbitClient.getAgentDispatchPacket(args)
|
|
305
|
-
},
|
|
306
|
-
{
|
|
307
|
-
name: "board_read_ticket",
|
|
308
|
-
description:
|
|
309
|
-
"Look up a ticket by ticket_id, number, or exact title (case-insensitive), and return its title, description, labels, state, and the board manual (agent_instructions + journal + deployment paths). Resolves board from board_id / board_slug / board first; the MCP session board is only a fallback convenience. Ticket comments are intentionally omitted; use board_read_comments for explicit comment retrieval.",
|
|
310
|
-
inputSchema: {
|
|
311
|
-
type: "object",
|
|
312
|
-
properties: {
|
|
313
|
-
ticket_id: { type: "string" },
|
|
314
|
-
number: { type: "integer" },
|
|
315
|
-
title: { type: "string" },
|
|
316
|
-
board_id: { type: "string" },
|
|
317
|
-
board_slug: { type: "string" },
|
|
318
|
-
board: { type: "string" }
|
|
319
|
-
},
|
|
320
|
-
additionalProperties: false
|
|
321
|
-
},
|
|
322
|
-
handler: (args) => orbitClient.readTicket(args)
|
|
323
|
-
},
|
|
324
|
-
{
|
|
325
|
-
name: "board_read_comments",
|
|
326
|
-
description:
|
|
327
|
-
"Return every comment on a ticket (oldest → newest). Look up by ticket_id, number, or exact title (case-insensitive). Resolves board from board_id / board_slug / board first; the MCP session board is only a fallback convenience. Comment kinds include human_comment, agent_note, checkpoint, completion, note.",
|
|
328
|
-
inputSchema: {
|
|
329
|
-
type: "object",
|
|
330
|
-
properties: {
|
|
331
|
-
ticket_id: { type: "string" },
|
|
332
|
-
number: { type: "integer" },
|
|
333
|
-
title: { type: "string" },
|
|
334
|
-
board_id: { type: "string" },
|
|
335
|
-
board_slug: { type: "string" },
|
|
336
|
-
board: { type: "string" }
|
|
337
|
-
},
|
|
338
|
-
additionalProperties: false
|
|
339
|
-
},
|
|
340
|
-
handler: (args) => orbitClient.readComments(args)
|
|
341
|
-
},
|
|
342
|
-
{
|
|
343
|
-
name: "board_get_ticket_relations",
|
|
344
|
-
description: "List every link touching a ticket. Resolves board from board_id / board_slug / board first; the MCP session board is only a fallback convenience. Each entry has `type`, `direction`, `other_ticket`, and `source`. `source='relation'` rows come from the relations table (`relates_to` / `blocks` / `blocked_by`) and are the SSOT for peer dependencies — write via `POST /api/relations`, delete via `DELETE /api/relations/:id`. `source='hierarchy'` rows are read-only synthetic entries surfacing epic ownership (`child_of` toward the parent epic, `parent_of` toward each child); they have `id: null` and are mutated by patching the ticket's `parent_ticket_id`, not by calling the relations endpoints.",
|
|
345
|
-
inputSchema: {
|
|
346
|
-
type: "object",
|
|
347
|
-
properties: {
|
|
348
|
-
ticket_id: { type: "string" },
|
|
349
|
-
board_id: { type: "string" },
|
|
350
|
-
board_slug: { type: "string" },
|
|
351
|
-
board: { type: "string" }
|
|
352
|
-
},
|
|
353
|
-
required: ["ticket_id"],
|
|
354
|
-
additionalProperties: false
|
|
355
|
-
},
|
|
356
|
-
handler: (args) => orbitClient.getTicketRelations(args)
|
|
357
|
-
},
|
|
358
|
-
{
|
|
359
|
-
name: "board_get_ticket_blockers",
|
|
360
|
-
description: "Return unresolved blockers for a ticket and a can_start boolean. Resolves board from board_id / board_slug / board first; the MCP session board is only a fallback convenience. The single source of truth for dependencies is the per-card `blocked_by` row in the relations table; if a row exists the blocker is unresolved. Rows are removed two ways: (1) automatically when the blocking ticket moves into the Done lane (state role='done'), or (2) manually by the user in the UI. A ticket with can_start=false must not be worked on until its blockers resolve. Epic blocker targets expand to that epic's open children (children in any lane whose role is not 'done', not archived); each expanded entry carries via_epic_id.",
|
|
361
|
-
inputSchema: {
|
|
362
|
-
type: "object",
|
|
363
|
-
properties: {
|
|
364
|
-
ticket_id: { type: "string" },
|
|
365
|
-
board_id: { type: "string" },
|
|
366
|
-
board_slug: { type: "string" },
|
|
367
|
-
board: { type: "string" }
|
|
368
|
-
},
|
|
369
|
-
required: ["ticket_id"],
|
|
370
|
-
additionalProperties: false
|
|
371
|
-
},
|
|
372
|
-
handler: (args) => orbitClient.getTicketBlockers(args)
|
|
373
|
-
},
|
|
374
|
-
{
|
|
375
|
-
name: "board_search",
|
|
376
|
-
description:
|
|
377
|
-
"Search tickets, comments, and implementation records by text on a specific board. Resolves board from board_id / board_slug / board first; the MCP session board is only a fallback convenience when selectors are omitted, and omitted selectors never merge results from other boards.",
|
|
378
|
-
inputSchema: {
|
|
379
|
-
type: "object",
|
|
380
|
-
properties: {
|
|
381
|
-
q: { type: "string" },
|
|
382
|
-
board: { type: "string" },
|
|
383
|
-
board_slug: { type: "string" },
|
|
384
|
-
board_id: { type: "string" },
|
|
385
|
-
limit: { type: "integer", minimum: 1, maximum: 50 },
|
|
386
|
-
mode: { type: "string", enum: ["ids", "summary", "full"] },
|
|
387
|
-
include_full: { type: "boolean" },
|
|
388
|
-
fields: { type: "array", items: { type: "string" } },
|
|
389
|
-
max_chars_per_field: { type: "integer", minimum: 1 }
|
|
390
|
-
},
|
|
391
|
-
required: ["q"],
|
|
392
|
-
additionalProperties: false
|
|
393
|
-
},
|
|
394
|
-
handler: (args) => orbitClient.search(args)
|
|
395
|
-
},
|
|
396
|
-
{
|
|
397
|
-
name: "board_create_ticket",
|
|
398
|
-
description:
|
|
399
|
-
"Create a new Orbit ticket/card. Resolves board from board_id / board_slug / board first; the MCP session board is only a fallback convenience. Use this instead of editing .orbit/board.db directly. Same operation as POST /api/tickets.",
|
|
400
|
-
inputSchema: {
|
|
401
|
-
type: "object",
|
|
402
|
-
properties: {
|
|
403
|
-
board_id: { type: "string" },
|
|
404
|
-
board_slug: { type: "string" },
|
|
405
|
-
board: { type: "string" },
|
|
406
|
-
title: { type: "string" },
|
|
407
|
-
description: { type: "string" },
|
|
408
|
-
type: { type: "string", enum: ["epic", "feature", "task", "bug"] },
|
|
409
|
-
parent_ticket_id: { type: ["string", "null"] },
|
|
410
|
-
ai_plan: { type: "string", description: "Existing AI Plan field: terse human-visible plan summary; link full markdown handoffs with plan_artifact_path." },
|
|
411
|
-
plan_artifact_path: { type: "string", description: "Human-editable link/path to a full markdown/text plan artifact, e.g. docs/plan.md or ./docs/plan.md. Keep AI Plan terse; do not paste full handoffs into ai_plan." },
|
|
412
|
-
implementation_summary: { type: "string", description: "Existing Work Summary field: terse human-visible result summary; link full reports with implementation_artifact_path." },
|
|
413
|
-
implementation_artifact_path: { type: "string", description: "Human-editable link/path to a full markdown/text implementation report or evidence artifact, e.g. docs/report.md or ./docs/report.md. Keep Work Summary terse." },
|
|
414
|
-
implementation_updates: { type: "string", description: "Revisions field: terse later changes/corrections after initial implementation; omit logs, chronology, and normal completion notes." },
|
|
415
|
-
state_id: { type: "string" },
|
|
416
|
-
priority: { type: "integer" },
|
|
417
|
-
labels: { type: "array", items: { type: "string" } }
|
|
418
|
-
},
|
|
419
|
-
required: ["title"],
|
|
420
|
-
additionalProperties: false
|
|
421
|
-
},
|
|
422
|
-
handler: (args) => orbitClient.createTicket(args)
|
|
423
|
-
},
|
|
424
|
-
{
|
|
425
|
-
name: "board_update_ticket",
|
|
426
|
-
description:
|
|
427
|
-
"Update ticket fields such as terse AI Plan / Work Summary text, linked artifact paths, Revisions, state, type, priority, or labels (full replace: array of label names). Resolves board from board_id / board_slug / board; the MCP session board is only a fallback convenience.",
|
|
428
|
-
inputSchema: {
|
|
429
|
-
type: "object",
|
|
430
|
-
properties: {
|
|
431
|
-
ticket_id: { type: "string" },
|
|
432
|
-
board_id: { type: "string" },
|
|
433
|
-
board_slug: { type: "string" },
|
|
434
|
-
board: { type: "string" },
|
|
435
|
-
title: { type: "string" },
|
|
436
|
-
description: { type: "string" },
|
|
437
|
-
type: { type: "string" },
|
|
438
|
-
parent_ticket_id: { type: ["string", "null"] },
|
|
439
|
-
ai_plan: { type: "string", description: "Existing AI Plan field: terse human-visible plan summary; link full markdown handoffs with plan_artifact_path." },
|
|
440
|
-
plan_artifact_path: { type: "string", description: "Human-editable link/path to a full markdown/text plan artifact, e.g. docs/plan.md or ./docs/plan.md. Keep AI Plan terse; do not paste full handoffs into ai_plan." },
|
|
441
|
-
implementation_summary: { type: "string", description: "Existing Work Summary field: terse human-visible result summary; link full reports with implementation_artifact_path." },
|
|
442
|
-
implementation_artifact_path: { type: "string", description: "Human-editable link/path to a full markdown/text implementation report or evidence artifact, e.g. docs/report.md or ./docs/report.md. Keep Work Summary terse." },
|
|
443
|
-
implementation_updates: { type: "string", description: "Revisions field: terse later changes/corrections after initial implementation; omit logs, chronology, and normal completion notes." },
|
|
444
|
-
state_id: { type: "string" },
|
|
445
|
-
priority: { type: "integer" },
|
|
446
|
-
labels: { type: "array", items: { type: "string" }, description: "Replaces all ticket labels when provided." }
|
|
447
|
-
},
|
|
448
|
-
required: ["ticket_id"],
|
|
449
|
-
additionalProperties: false
|
|
450
|
-
},
|
|
451
|
-
handler: ({ ticket_id, ...patch }) => orbitClient.updateTicket({ ticket_id, ...patch })
|
|
452
|
-
},
|
|
453
|
-
{
|
|
454
|
-
name: "board_add_comment",
|
|
455
|
-
description: "Add a ticket comment or agent breadcrumb. Resolves board from board_id / board_slug / board; the MCP session board is only a fallback convenience.",
|
|
456
|
-
inputSchema: {
|
|
457
|
-
type: "object",
|
|
458
|
-
properties: {
|
|
459
|
-
ticket_id: { type: "string" },
|
|
460
|
-
board_id: { type: "string" },
|
|
461
|
-
board_slug: { type: "string" },
|
|
462
|
-
board: { type: "string" },
|
|
463
|
-
body: { type: "string" },
|
|
464
|
-
kind: { type: "string" }
|
|
465
|
-
},
|
|
466
|
-
required: ["ticket_id", "body"],
|
|
467
|
-
additionalProperties: false
|
|
468
|
-
},
|
|
469
|
-
handler: ({ ticket_id, ...body }) => orbitClient.addComment({ ticket_id, ...body })
|
|
470
|
-
},
|
|
471
|
-
{
|
|
472
|
-
name: "board_create_review_verdict",
|
|
473
|
-
description: "Create a durable Sentinel/agent review verdict for a ticket. Verdict must be PASS, BLOCK, or QUESTION. Findings and evidence commands are stored as structured arrays; comments may mirror the data for humans but are not the source of truth. Resolves board from board_id / board_slug / board; the active session board is only a fallback convenience.",
|
|
474
|
-
inputSchema: {
|
|
475
|
-
type: "object",
|
|
476
|
-
properties: {
|
|
477
|
-
ticket_id: { type: "string" },
|
|
478
|
-
board_id: { type: "string" },
|
|
479
|
-
board_slug: { type: "string" },
|
|
480
|
-
board: { type: "string" },
|
|
481
|
-
verdict: { type: "string", enum: ["PASS", "BLOCK", "QUESTION"] },
|
|
482
|
-
blocking_findings: { type: "array" },
|
|
483
|
-
optional_findings: { type: "array" },
|
|
484
|
-
evidence_commands: { type: "array" },
|
|
485
|
-
reviewer_profile: { type: "string" },
|
|
486
|
-
reviewer_session_id: { type: "string" },
|
|
487
|
-
reviewed_commit_sha: { type: "string" },
|
|
488
|
-
dispatch_run_id: { type: "string" },
|
|
489
|
-
supersedes_prior_review_id: { type: ["string", "null"] }
|
|
490
|
-
},
|
|
491
|
-
required: ["ticket_id", "verdict"],
|
|
492
|
-
additionalProperties: false
|
|
493
|
-
},
|
|
494
|
-
handler: ({ ticket_id, ...body }) => orbitClient.createReviewVerdict({ ticket_id, ...body })
|
|
495
|
-
},
|
|
496
|
-
{
|
|
497
|
-
name: "board_list_review_verdicts",
|
|
498
|
-
description: "List durable review verdict records for a ticket, newest first. Use this for repair/re-review tooling instead of parsing prose comments.",
|
|
499
|
-
inputSchema: {
|
|
500
|
-
type: "object",
|
|
501
|
-
properties: {
|
|
502
|
-
ticket_id: { type: "string" },
|
|
503
|
-
board_id: { type: "string" },
|
|
504
|
-
board_slug: { type: "string" },
|
|
505
|
-
board: { type: "string" },
|
|
506
|
-
limit: { type: "integer", minimum: 1, maximum: 200 }
|
|
507
|
-
},
|
|
508
|
-
required: ["ticket_id"],
|
|
509
|
-
additionalProperties: false
|
|
510
|
-
},
|
|
511
|
-
handler: (args) => orbitClient.listReviewVerdicts(args)
|
|
512
|
-
},
|
|
513
|
-
{
|
|
514
|
-
name: "board_get_review_verdict",
|
|
515
|
-
description: "Read one durable review verdict record by id.",
|
|
516
|
-
inputSchema: {
|
|
517
|
-
type: "object",
|
|
518
|
-
properties: {
|
|
519
|
-
review_id: { type: "string" },
|
|
520
|
-
board_id: { type: "string" },
|
|
521
|
-
board_slug: { type: "string" },
|
|
522
|
-
board: { type: "string" }
|
|
523
|
-
},
|
|
524
|
-
required: ["review_id"],
|
|
525
|
-
additionalProperties: false
|
|
526
|
-
},
|
|
527
|
-
handler: (args) => orbitClient.getReviewVerdict(args)
|
|
528
|
-
},
|
|
529
|
-
{
|
|
530
|
-
name: "board_add_board_entry",
|
|
531
|
-
description: "Add a durable board-level decision or lesson for future agents. Resolves board from board_id / board_slug / board first; the MCP session board is only a fallback convenience.",
|
|
532
|
-
inputSchema: {
|
|
533
|
-
type: "object",
|
|
534
|
-
properties: {
|
|
535
|
-
board_id: { type: "string" },
|
|
536
|
-
board_slug: { type: "string" },
|
|
537
|
-
board: { type: "string" },
|
|
538
|
-
type: { type: "string", enum: ["decision", "lesson"] },
|
|
539
|
-
title: { type: "string" },
|
|
540
|
-
body: { type: "string" },
|
|
541
|
-
ticket_id: { type: "string" }
|
|
542
|
-
},
|
|
543
|
-
required: ["type", "title"],
|
|
544
|
-
additionalProperties: false
|
|
545
|
-
},
|
|
546
|
-
handler: (args) => orbitClient.addBoardEntry(args)
|
|
547
|
-
},
|
|
548
|
-
{
|
|
549
|
-
name: "board_checkpoint",
|
|
550
|
-
description:
|
|
551
|
-
"Pause mid-flight for a human: moves the ticket to Review (same lane as board_complete; checkpoint distinguishes a blocking question) and posts a checkpoint-kind comment with your message. Use when you cannot proceed without the user—not for normal 'work is done' handoff (use board_complete). Resolves board from board_id / board_slug / board; the MCP session board is only a fallback convenience.",
|
|
552
|
-
inputSchema: {
|
|
553
|
-
type: "object",
|
|
554
|
-
properties: {
|
|
555
|
-
ticket_id: { type: "string" },
|
|
556
|
-
board_id: { type: "string" },
|
|
557
|
-
board_slug: { type: "string" },
|
|
558
|
-
board: { type: "string" },
|
|
559
|
-
message: { type: "string" }
|
|
560
|
-
},
|
|
561
|
-
required: ["ticket_id", "message"],
|
|
562
|
-
additionalProperties: false
|
|
563
|
-
},
|
|
564
|
-
handler: (args) => orbitClient.checkpoint(args)
|
|
565
|
-
},
|
|
566
|
-
{
|
|
567
|
-
name: "board_complete",
|
|
568
|
-
description:
|
|
569
|
-
"Hand off finished work for human review: moves the ticket to the Review lane (by role or name), writes a compact completion comment, stores summary as the terse Work Summary (`implementation_summary`), and accepts optional linked implementation_artifact_path. Pass updates only for Revisions: terse later changes/corrections after initial implementation, not normal completion notes. This is the usual 'ready for you to look' path; use board_checkpoint only when blocked mid-flight. Resolves board from board_id / board_slug / board; the MCP session board is only a fallback convenience.",
|
|
570
|
-
inputSchema: {
|
|
571
|
-
type: "object",
|
|
572
|
-
properties: {
|
|
573
|
-
ticket_id: { type: "string" },
|
|
574
|
-
board_id: { type: "string" },
|
|
575
|
-
board_slug: { type: "string" },
|
|
576
|
-
board: { type: "string" },
|
|
577
|
-
summary: { type: "string", description: "Terse human-visible Work Summary stored in implementation_summary." },
|
|
578
|
-
updates: { type: "string", description: "Optional Revisions text only: terse later changes/corrections after initial implementation." },
|
|
579
|
-
implementation_artifact_path: { type: "string", description: "Human-editable link/path to a markdown/text implementation report artifact; can point at a report generated outside Orbit." },
|
|
580
|
-
summary_artifact_path: { type: "string", description: "Compatibility alias for implementation_artifact_path." },
|
|
581
|
-
pr_url: { type: "string" },
|
|
582
|
-
next_state: { type: "string" }
|
|
583
|
-
},
|
|
584
|
-
required: ["ticket_id", "summary"],
|
|
585
|
-
additionalProperties: false
|
|
586
|
-
},
|
|
587
|
-
handler: (args) => orbitClient.complete(args)
|
|
588
|
-
},
|
|
589
|
-
{
|
|
590
|
-
name: "board_archive_ticket",
|
|
591
|
-
description:
|
|
592
|
-
"Archive (soft-delete) a ticket. The ticket is moved off the board into the Archive: it is excluded from bootstrap, search, claim-next, blockers, and relations, but its data and comments are preserved. Reverse with board_restore_ticket; permanently remove with board_delete_ticket. Resolves board from board_id / board_slug / board; the MCP session board is only a fallback convenience.",
|
|
593
|
-
inputSchema: {
|
|
594
|
-
type: "object",
|
|
595
|
-
properties: { ticket_id: { type: "string" }, board_id: { type: "string" }, board_slug: { type: "string" }, board: { type: "string" } },
|
|
596
|
-
required: ["ticket_id"],
|
|
597
|
-
additionalProperties: false
|
|
598
|
-
},
|
|
599
|
-
handler: (args) => orbitClient.archiveTicket(args)
|
|
600
|
-
},
|
|
601
|
-
{
|
|
602
|
-
name: "board_restore_ticket",
|
|
603
|
-
description: "Restore an archived ticket back to the board in its previous lane (state). Resolves board from board_id / board_slug / board; the MCP session board is only a fallback convenience.",
|
|
604
|
-
inputSchema: {
|
|
605
|
-
type: "object",
|
|
606
|
-
properties: { ticket_id: { type: "string" }, board_id: { type: "string" }, board_slug: { type: "string" }, board: { type: "string" } },
|
|
607
|
-
required: ["ticket_id"],
|
|
608
|
-
additionalProperties: false
|
|
609
|
-
},
|
|
610
|
-
handler: (args) => orbitClient.restoreTicket(args)
|
|
611
|
-
},
|
|
612
|
-
{
|
|
613
|
-
name: "board_delete_ticket",
|
|
614
|
-
description:
|
|
615
|
-
"Permanently delete a ticket. The ticket MUST already be archived (call board_archive_ticket first) — otherwise this returns 409 ticket_not_archived. This cannot be undone: comments, labels, and relations are removed via cascade. Events for this ticket are kept but their ticket_id becomes NULL. Resolves board from board_id / board_slug / board; the MCP session board is only a fallback convenience.",
|
|
616
|
-
inputSchema: {
|
|
617
|
-
type: "object",
|
|
618
|
-
properties: { ticket_id: { type: "string" }, board_id: { type: "string" }, board_slug: { type: "string" }, board: { type: "string" } },
|
|
619
|
-
required: ["ticket_id"],
|
|
620
|
-
additionalProperties: false
|
|
621
|
-
},
|
|
622
|
-
handler: (args) => orbitClient.deleteTicket(args)
|
|
623
|
-
},
|
|
624
|
-
{
|
|
625
|
-
name: "board_list_archive",
|
|
626
|
-
description: "List archived tickets for a board, ordered by archive time (most recent first). Identify the board with board_id or board_slug.",
|
|
627
|
-
inputSchema: {
|
|
628
|
-
type: "object",
|
|
629
|
-
properties: {
|
|
630
|
-
board_id: { type: "string" },
|
|
631
|
-
board_slug: { type: "string" }
|
|
632
|
-
},
|
|
633
|
-
additionalProperties: false
|
|
634
|
-
},
|
|
635
|
-
handler: (args) => orbitClient.listArchive(args)
|
|
636
|
-
},
|
|
637
|
-
{
|
|
638
|
-
name: "board_export_board",
|
|
639
|
-
description: "Export a board snapshot as JSON for backup or transfer. Resolves board from board_id / board_slug / board first; the MCP session board is only a fallback convenience.",
|
|
640
|
-
inputSchema: {
|
|
641
|
-
type: "object",
|
|
642
|
-
properties: {
|
|
643
|
-
board_id: { type: "string" },
|
|
644
|
-
board_slug: { type: "string" },
|
|
645
|
-
board: { type: "string" },
|
|
646
|
-
include_attachments: { type: "boolean", description: "Embed attached image bytes in the JSON snapshot." },
|
|
647
|
-
include_images: { type: "boolean", description: "Alias for include_attachments." }
|
|
648
|
-
},
|
|
649
|
-
additionalProperties: false
|
|
650
|
-
},
|
|
651
|
-
handler: (args) => orbitClient.exportBoard(args)
|
|
652
|
-
},
|
|
653
|
-
{
|
|
654
|
-
name: "board_update_settings",
|
|
655
|
-
description:
|
|
656
|
-
"PATCH-equivalent for board settings: name (display rename only; slug/canonical URLs stay unchanged), repo_url, system_path, default_branch, and agent_instructions (project-level agent context). Resolves board from board_id / board_slug / board; the MCP session board is only a fallback convenience.",
|
|
657
|
-
inputSchema: {
|
|
658
|
-
type: "object",
|
|
659
|
-
properties: {
|
|
660
|
-
board_id: { type: "string" },
|
|
661
|
-
board_slug: { type: "string" },
|
|
662
|
-
board: { type: "string" },
|
|
663
|
-
name: { type: "string" },
|
|
664
|
-
repo_url: { type: "string" },
|
|
665
|
-
system_path: { type: "string" },
|
|
666
|
-
default_branch: { type: "string" },
|
|
667
|
-
agent_instructions: { type: "string" }
|
|
668
|
-
},
|
|
669
|
-
additionalProperties: false
|
|
670
|
-
},
|
|
671
|
-
handler: (args) => orbitClient.updateSettings(args)
|
|
672
|
-
}
|
|
673
|
-
];
|
|
674
|
-
|
|
675
|
-
const tools = new Map(TOOL_DEFS.map((tool) => [tool.name, tool]));
|
|
676
|
-
|
|
677
|
-
let inputBuffer = Buffer.alloc(0);
|
|
678
|
-
let expectedBodyLength = -1;
|
|
679
|
-
let shuttingDown = false;
|
|
680
|
-
|
|
681
|
-
function shutdown(code = 0) {
|
|
682
|
-
if (shuttingDown) return;
|
|
683
|
-
shuttingDown = true;
|
|
684
|
-
orbitClient.close?.();
|
|
685
|
-
process.exit(code);
|
|
686
|
-
}
|
|
687
|
-
|
|
688
|
-
process.stdin.on("data", (chunk) => {
|
|
689
|
-
inputBuffer = Buffer.concat([inputBuffer, chunk]);
|
|
690
|
-
drainInput();
|
|
691
|
-
});
|
|
692
|
-
|
|
693
|
-
process.stdin.on("end", () => shutdown(0));
|
|
694
|
-
process.stdin.resume();
|
|
695
|
-
|
|
696
|
-
function drainInput() {
|
|
697
|
-
while (true) {
|
|
698
|
-
if (expectedBodyLength >= 0) {
|
|
699
|
-
if (inputBuffer.byteLength < expectedBodyLength) return;
|
|
700
|
-
const body = inputBuffer.slice(0, expectedBodyLength);
|
|
701
|
-
inputBuffer = inputBuffer.slice(expectedBodyLength);
|
|
702
|
-
expectedBodyLength = -1;
|
|
703
|
-
handleMessage(body.toString("utf8"), "content-length");
|
|
704
|
-
continue;
|
|
705
|
-
}
|
|
706
|
-
|
|
707
|
-
const preview = inputBuffer.slice(0, Math.min(inputBuffer.byteLength, 32)).toString("utf8").toLowerCase();
|
|
708
|
-
if (preview.startsWith("content-length:")) {
|
|
709
|
-
const headerEnd = inputBuffer.indexOf(Buffer.from("\r\n\r\n"));
|
|
710
|
-
if (headerEnd === -1) return;
|
|
711
|
-
|
|
712
|
-
const headerText = inputBuffer.slice(0, headerEnd).toString("utf8");
|
|
713
|
-
inputBuffer = inputBuffer.slice(headerEnd + 4);
|
|
714
|
-
const headers = parseHeaders(headerText);
|
|
715
|
-
expectedBodyLength = Number(headers["content-length"] || -1);
|
|
716
|
-
if (!Number.isFinite(expectedBodyLength) || expectedBodyLength < 0) {
|
|
717
|
-
writeMessage(errorResponse(null, -32700, "Missing Content-Length header."), "content-length");
|
|
718
|
-
expectedBodyLength = -1;
|
|
719
|
-
}
|
|
720
|
-
continue;
|
|
721
|
-
}
|
|
722
|
-
|
|
723
|
-
const lineEnd = inputBuffer.indexOf(Buffer.from("\n"));
|
|
724
|
-
if (lineEnd === -1) return;
|
|
725
|
-
const line = inputBuffer.slice(0, lineEnd).toString("utf8").replace(/\r$/, "");
|
|
726
|
-
inputBuffer = inputBuffer.slice(lineEnd + 1);
|
|
727
|
-
if (!line.trim()) continue;
|
|
728
|
-
handleMessage(line, "line");
|
|
729
|
-
}
|
|
730
|
-
}
|
|
731
|
-
|
|
732
|
-
function parseHeaders(text) {
|
|
733
|
-
const headers = {};
|
|
734
|
-
for (const line of text.split("\r\n")) {
|
|
735
|
-
const index = line.indexOf(":");
|
|
736
|
-
if (index === -1) continue;
|
|
737
|
-
const key = line.slice(0, index).trim().toLowerCase();
|
|
738
|
-
const value = line.slice(index + 1).trim();
|
|
739
|
-
headers[key] = value;
|
|
740
|
-
}
|
|
741
|
-
return headers;
|
|
742
|
-
}
|
|
743
|
-
|
|
744
|
-
async function handleMessage(body, transport = "content-length") {
|
|
745
|
-
let message;
|
|
746
|
-
try {
|
|
747
|
-
message = JSON.parse(body);
|
|
748
|
-
} catch {
|
|
749
|
-
writeMessage(errorResponse(null, -32700, "Invalid JSON."), transport);
|
|
750
|
-
return;
|
|
751
|
-
}
|
|
752
|
-
|
|
753
|
-
if (!message || typeof message !== "object") {
|
|
754
|
-
writeMessage(errorResponse(null, -32600, "Invalid request."), transport);
|
|
755
|
-
return;
|
|
756
|
-
}
|
|
757
|
-
|
|
758
|
-
if (!("id" in message)) {
|
|
759
|
-
if (message.method === "notifications/initialized") return;
|
|
760
|
-
return;
|
|
761
|
-
}
|
|
762
|
-
|
|
763
|
-
try {
|
|
764
|
-
const result = await dispatch(message);
|
|
765
|
-
writeMessage({
|
|
766
|
-
jsonrpc: "2.0",
|
|
767
|
-
id: message.id,
|
|
768
|
-
result
|
|
769
|
-
}, transport);
|
|
770
|
-
} catch (error) {
|
|
771
|
-
writeMessage(errorResponse(message.id, error.code || -32000, error.message || "Internal error."), transport);
|
|
772
|
-
} finally {
|
|
773
|
-
orbitClient.close?.();
|
|
774
|
-
}
|
|
775
|
-
}
|
|
776
|
-
|
|
777
|
-
async function dispatch(message) {
|
|
778
|
-
switch (message.method) {
|
|
779
|
-
case "initialize":
|
|
780
|
-
return {
|
|
781
|
-
protocolVersion: message.params?.protocolVersion || "2024-11-05",
|
|
782
|
-
capabilities: {
|
|
783
|
-
tools: {}
|
|
784
|
-
},
|
|
785
|
-
serverInfo: SERVER_INFO
|
|
786
|
-
};
|
|
787
|
-
case "ping":
|
|
788
|
-
return {};
|
|
789
|
-
case "tools/list":
|
|
790
|
-
return {
|
|
791
|
-
tools: TOOL_DEFS.map(({ name, description, inputSchema }) => ({
|
|
792
|
-
name,
|
|
793
|
-
description,
|
|
794
|
-
inputSchema
|
|
795
|
-
}))
|
|
796
|
-
};
|
|
797
|
-
case "tools/call":
|
|
798
|
-
return callTool(message.params || {});
|
|
799
|
-
default:
|
|
800
|
-
throw rpcError(-32601, `Method not found: ${message.method}`);
|
|
801
|
-
}
|
|
802
|
-
}
|
|
803
|
-
|
|
804
|
-
async function callTool(params) {
|
|
805
|
-
const name = params.name;
|
|
806
|
-
const args = params.arguments || {};
|
|
807
|
-
const tool = tools.get(name);
|
|
808
|
-
if (!tool) throw rpcError(-32601, `Unknown tool: ${name}`);
|
|
809
|
-
|
|
810
|
-
let result;
|
|
811
|
-
try {
|
|
812
|
-
result = await tool.handler(args);
|
|
813
|
-
} catch (error) {
|
|
814
|
-
return {
|
|
815
|
-
content: [
|
|
816
|
-
{
|
|
817
|
-
type: "text",
|
|
818
|
-
text: error.message || "Tool call failed."
|
|
819
|
-
}
|
|
820
|
-
],
|
|
821
|
-
isError: true
|
|
822
|
-
};
|
|
823
|
-
}
|
|
824
|
-
|
|
825
|
-
return {
|
|
826
|
-
content: [
|
|
827
|
-
{
|
|
828
|
-
type: "text",
|
|
829
|
-
text: JSON.stringify(result, null, 2)
|
|
830
|
-
}
|
|
831
|
-
]
|
|
832
|
-
};
|
|
833
|
-
}
|
|
834
|
-
|
|
835
|
-
function writeMessage(message, transport = "content-length") {
|
|
836
|
-
const json = JSON.stringify(message);
|
|
837
|
-
if (transport === "line") {
|
|
838
|
-
process.stdout.write(`${json}\n`);
|
|
839
|
-
return;
|
|
840
|
-
}
|
|
841
|
-
process.stdout.write(`Content-Length: ${Buffer.byteLength(json, "utf8")}\r\n\r\n${json}`);
|
|
842
|
-
}
|
|
843
|
-
|
|
844
|
-
function errorResponse(id, code, message) {
|
|
845
|
-
return {
|
|
846
|
-
jsonrpc: "2.0",
|
|
847
|
-
id,
|
|
848
|
-
error: {
|
|
849
|
-
code,
|
|
850
|
-
message
|
|
851
|
-
}
|
|
852
|
-
};
|
|
853
|
-
}
|
|
854
|
-
|
|
855
|
-
function rpcError(code, message) {
|
|
856
|
-
const error = new Error(message);
|
|
857
|
-
error.code = code;
|
|
858
|
-
return error;
|
|
859
|
-
}
|
|
860
|
-
|
|
861
|
-
process.on("uncaughtException", (error) => {
|
|
862
|
-
writeMessage(errorResponse(null, -32000, error.message || "Uncaught exception."));
|
|
863
|
-
});
|
|
864
|
-
|
|
865
|
-
process.on("unhandledRejection", (error) => {
|
|
866
|
-
writeMessage(errorResponse(null, -32000, error?.message || "Unhandled rejection."));
|
|
867
|
-
});
|
|
868
|
-
|
|
869
|
-
process.once("SIGINT", () => shutdown(0));
|
|
870
|
-
process.once("SIGTERM", () => shutdown(0));
|
|
871
|
-
process.once("exit", () => orbitClient.close?.());
|
|
872
|
-
|
|
873
|
-
if (process.env.MAB_MCP_STDERR_LOG === "1") {
|
|
874
|
-
const target = typeof orbitClient.sessionLabel === "function" ? orbitClient.sessionLabel() : orbitClient.sessionLabel;
|
|
875
|
-
console.error(`Starscape Orbit MCP ready. Mode: ${orbitClient.mode}. Target: ${target}`);
|
|
876
|
-
}
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Orbit 1.x's MCP server lived here, and 1.x's Settings, AI told people to register it as
|
|
3
|
+
// `node <this file>`, naming the project in PROJECT_ROOT (or by the working folder). Orbit 2.0
|
|
4
|
+
// installs over 1.x in place, so this file keeps those registrations starting: it runs 2.0's
|
|
5
|
+
// `orbit mcp`, with PROJECT_ROOT as its --cwd. 1.x's remote settings (ORBIT_MODE, ORBIT_API_URL,
|
|
6
|
+
// ORBIT_DEFAULT_BOARD) mean the same in 2.0 and pass through. It says so on stderr only, because
|
|
7
|
+
// stdout is the MCP protocol. It stays until 3.0. Plain JavaScript, outside tsc and lint: npm
|
|
8
|
+
// ships it as it is. Its twin for 1.x's bin is src/cli/orbit.js.
|
|
9
|
+
|
|
10
|
+
const args = ["mcp"];
|
|
11
|
+
if (process.env.PROJECT_ROOT) args.push("--cwd", process.env.PROJECT_ROOT);
|
|
12
|
+
process.argv.splice(2, process.argv.length - 2, ...args);
|
|
13
|
+
|
|
14
|
+
process.stderr.write("Orbit 2.0 starts as `orbit mcp`: this server was added the 1.x way, so re-add it as `orbit mcp` when convenient.\n");
|
|
15
|
+
|
|
16
|
+
await import("../lib/cli/orbit.js");
|