@starlove/orbit 1.0.7 → 1.1.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/README.md +22 -7
- package/SKILL-ORBIT.md +26 -1
- package/dist/full/index.html +2 -2
- package/dist/full/js/app.a4f045fc.js +676 -0
- package/dist/full/js/app.a4f045fc.js.map +7 -0
- package/dist/full/{styles.14e9dbbd.css → styles.836872e7.css} +836 -39
- package/dist/full/version.json +2 -2
- package/docs/AGENT_PROTOCOL.md +23 -5
- package/docs/MCP_SETUP.md +5 -3
- package/docs/ORBIT_DISPATCH.md +185 -0
- package/docs/setup/orbit-init-wizard.md +174 -0
- package/package.json +2 -2
- package/public/js/actor-labels.js +65 -0
- package/public/js/app.js +8 -1
- package/public/js/card-actions.js +526 -0
- package/public/js/config.js +2 -1
- package/public/js/format.js +204 -10
- package/public/js/kanban.js +15 -0
- package/public/js/local-backend.js +24 -8
- package/public/js/router.js +3 -2
- package/public/js/search.js +6 -3
- package/public/js/settings.js +96 -26
- package/public/js/state.js +50 -0
- package/public/js/ticket-detail.js +428 -125
- package/public/styles.css +836 -39
- package/src/cli/orbit.js +217 -32
- package/src/core/agent.js +1 -18
- package/src/core/agents-md.js +15 -10
- package/src/core/attachments.js +238 -0
- package/src/core/auth.js +36 -0
- package/src/core/backups.js +9 -3
- package/src/core/board.js +31 -0
- package/src/core/boards.js +1 -1
- package/src/core/db.js +42 -2
- package/src/core/delete-board.js +87 -21
- package/src/core/dispatch.js +532 -0
- package/src/core/http.js +31 -11
- package/src/core/migrate.js +198 -0
- package/src/core/paths.js +6 -0
- package/src/core/registry.js +12 -9
- package/src/core/router.js +61 -5
- package/src/core/search.js +3 -3
- package/src/core/snapshots.js +14 -2
- package/src/mcp/orbit-client.js +81 -38
- package/src/mcp-server.js +190 -6
- package/src/server.js +20 -0
- package/dist/full/js/app.f4fd5e94.js +0 -604
- package/dist/full/js/app.f4fd5e94.js.map +0 -7
package/src/cli/orbit.js
CHANGED
|
@@ -1,36 +1,55 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
3
|
* Orbit CLI — `orbit init` provisions `.orbit/board.db`, registry row, and
|
|
4
|
-
* copies SKILL-ORBIT.md into the target repo. Does not import board.js.
|
|
4
|
+
* copies managed SKILL-ORBIT.md into the target repo. Does not import board.js.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import { spawnSync } from "node:child_process";
|
|
8
|
-
import {
|
|
9
|
-
import { dirname, resolve } from "node:path";
|
|
8
|
+
import { existsSync, mkdirSync, readFileSync, rmSync, unlinkSync } from "node:fs";
|
|
9
|
+
import { dirname, join, resolve } from "node:path";
|
|
10
10
|
import { fileURLToPath } from "node:url";
|
|
11
|
-
import { removeOrbitAgentsSection, syncAgentsMd } from "../core/agents-md.js";
|
|
11
|
+
import { removeOrbitAgentsSection, syncAgentsMd, syncSkillOrbitMd } from "../core/agents-md.js";
|
|
12
12
|
|
|
13
13
|
const PACKAGE_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), "..", "..");
|
|
14
14
|
|
|
15
15
|
function printUsage() {
|
|
16
16
|
console.log(`Usage:
|
|
17
|
+
orbit -v, --version Print the Orbit CLI version
|
|
17
18
|
orbit init [options] Provision .orbit/board.db + SKILL-ORBIT.md + AGENTS.md
|
|
18
19
|
orbit reset [options] Delete .orbit/ + SKILL-ORBIT.md + registry row
|
|
19
20
|
orbit serve [options] Start the Orbit web app + HTTP API
|
|
20
21
|
orbit docker [options] Build/run Orbit's web app in Docker
|
|
21
22
|
orbit mcp [options] Start the stdio MCP server (for agent clients)
|
|
23
|
+
orbit dispatch [options] Dispatch a Hermes profile on a ticket
|
|
22
24
|
|
|
23
|
-
Options (init / reset / serve / docker / mcp):
|
|
25
|
+
Options (init / reset / serve / docker / mcp / dispatch):
|
|
24
26
|
--cwd <dir> Project root (default: process.cwd())
|
|
25
27
|
|
|
26
28
|
Options (init only):
|
|
27
29
|
--no-ai Create the board with AI collaboration disabled
|
|
28
30
|
--example Create example onboarding tickets
|
|
29
|
-
--refresh-agents-md Overwrite SKILL-ORBIT.md and refresh Orbit section in AGENTS.md
|
|
30
31
|
|
|
31
32
|
Options (serve / docker):
|
|
32
33
|
--port <n> HTTP port (default: 3337, or $PORT)
|
|
33
34
|
|
|
35
|
+
Options (dispatch):
|
|
36
|
+
--board <slug-or-id> Local board to dispatch against (default: board for --cwd)
|
|
37
|
+
--ticket <number-id> Ticket number or id to dispatch
|
|
38
|
+
--profile <name> Hermes profile to run (default: agent)
|
|
39
|
+
--policy <name> Autonomous policy wrappers to apply (default: agent-safe; use none to disable)
|
|
40
|
+
--server-url <url> Refused for now; dispatch is local-board only
|
|
41
|
+
--remote Refused for now; use remote MCP/manual orchestration for hosted boards
|
|
42
|
+
--worktree Create and preserve a git worktree for review/testing
|
|
43
|
+
--worktree-path <dir> Worktree path when --worktree is used
|
|
44
|
+
--branch <name> Branch name for the preserved worktree
|
|
45
|
+
--hermes-bin <cmd> Hermes executable (default: hermes)
|
|
46
|
+
--no-spawn Prepare card/worktree/run record but do not start Hermes or move In Progress
|
|
47
|
+
--no-yolo Do not pass --yolo to Hermes (default dispatch passes --yolo)
|
|
48
|
+
--foreground Attach spawned Hermes process to this terminal
|
|
49
|
+
--keep-handoff-file Keep duplicate handoff file under DATA_DIR/dispatch-runs/<board>
|
|
50
|
+
--force Dispatch even when blockers exist
|
|
51
|
+
--dry-run Preview only; no files, worktrees, ticket fields, comments, or agents change
|
|
52
|
+
|
|
34
53
|
Options (docker only):
|
|
35
54
|
--image <name> Docker image tag (default: starscape-orbit:local)
|
|
36
55
|
--data-dir <dir> Docker registry/export data dir (default: <cwd>/.orbit/docker-data)
|
|
@@ -41,6 +60,11 @@ Options (docker only):
|
|
|
41
60
|
--dry-run Print docker commands without running them`);
|
|
42
61
|
}
|
|
43
62
|
|
|
63
|
+
function printVersion() {
|
|
64
|
+
const packageJson = JSON.parse(readFileSync(resolve(PACKAGE_ROOT, "package.json"), "utf8"));
|
|
65
|
+
console.log(packageJson.version);
|
|
66
|
+
}
|
|
67
|
+
|
|
44
68
|
function shellQuote(value) {
|
|
45
69
|
return `"${String(value || "").replace(/"/g, '\\"')}"`;
|
|
46
70
|
}
|
|
@@ -59,7 +83,7 @@ async function loadCliCore() {
|
|
|
59
83
|
const [
|
|
60
84
|
{ disableAiCollaboration, enableAiCollaboration },
|
|
61
85
|
{ backupBoardDatabase, backupRegistry },
|
|
62
|
-
{ createRegistrySchema, openConnection },
|
|
86
|
+
{ closeConnection, createRegistrySchema, openConnection },
|
|
63
87
|
{ DATA_DIR, ROOT_DIR },
|
|
64
88
|
{ provisionRepoBoard },
|
|
65
89
|
{ deleteBoard, getBoardByRepoPath },
|
|
@@ -79,6 +103,7 @@ async function loadCliCore() {
|
|
|
79
103
|
ROOT_DIR,
|
|
80
104
|
backupBoardDatabase,
|
|
81
105
|
backupRegistry,
|
|
106
|
+
closeConnection,
|
|
82
107
|
createRegistrySchema,
|
|
83
108
|
deleteBoard,
|
|
84
109
|
disableAiCollaboration,
|
|
@@ -107,12 +132,36 @@ function disableAiIfRequested(options, registryRow, core) {
|
|
|
107
132
|
console.log("AI collaboration disabled for this board.");
|
|
108
133
|
}
|
|
109
134
|
|
|
135
|
+
function isBusyFilesystemError(error) {
|
|
136
|
+
const message = String(error?.message || "").toLowerCase();
|
|
137
|
+
return (
|
|
138
|
+
["EBUSY", "EPERM", "ENOTEMPTY"].includes(error?.code) ||
|
|
139
|
+
(error?.code === "ERR_SQLITE_ERROR" && (message.includes("locked") || message.includes("busy")))
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function resetBusyError(error, projectRoot) {
|
|
144
|
+
if (!isBusyFilesystemError(error)) return error;
|
|
145
|
+
const message = [
|
|
146
|
+
`Orbit could not remove local board files under ${projectRoot}.`,
|
|
147
|
+
"",
|
|
148
|
+
"On Windows this usually means an Orbit web server, MCP helper, editor, or terminal still has .orbit/board.db open.",
|
|
149
|
+
"Close Orbit server terminals and restart any AI client with Orbit MCP enabled, then rerun:",
|
|
150
|
+
` orbit reset --cwd ${shellQuote(projectRoot)}`,
|
|
151
|
+
"",
|
|
152
|
+
`Original error: ${error.message}`
|
|
153
|
+
].join("\n");
|
|
154
|
+
const wrapped = new Error(message);
|
|
155
|
+
wrapped.code = error.code;
|
|
156
|
+
wrapped.cause = error;
|
|
157
|
+
return wrapped;
|
|
158
|
+
}
|
|
159
|
+
|
|
110
160
|
function parseArgs(argv) {
|
|
111
161
|
const args = {
|
|
112
162
|
command: null,
|
|
113
163
|
cwd: process.env.PROJECT_ROOT ? resolve(process.env.PROJECT_ROOT) : process.cwd(),
|
|
114
164
|
cwdProvided: false,
|
|
115
|
-
refreshAgentsMd: false,
|
|
116
165
|
ai: true,
|
|
117
166
|
example: false,
|
|
118
167
|
port: null,
|
|
@@ -121,18 +170,43 @@ function parseArgs(argv) {
|
|
|
121
170
|
containerName: null,
|
|
122
171
|
buildImage: true,
|
|
123
172
|
detach: true,
|
|
124
|
-
dryRun: false
|
|
173
|
+
dryRun: false,
|
|
174
|
+
board: null,
|
|
175
|
+
ticket: null,
|
|
176
|
+
profile: "agent",
|
|
177
|
+
policy: "agent-safe",
|
|
178
|
+
serverUrl: null,
|
|
179
|
+
worktree: false,
|
|
180
|
+
worktreePath: null,
|
|
181
|
+
branch: null,
|
|
182
|
+
hermesBin: "hermes",
|
|
183
|
+
noSpawn: false,
|
|
184
|
+
yolo: true,
|
|
185
|
+
keepHandoffFile: false,
|
|
186
|
+
force: false,
|
|
187
|
+
foreground: false,
|
|
188
|
+
remote: false,
|
|
189
|
+
help: false,
|
|
190
|
+
invalid: false
|
|
125
191
|
};
|
|
126
192
|
const rest = argv.slice(2);
|
|
127
193
|
if (rest.length === 0 || rest[0] === "-h" || rest[0] === "--help") {
|
|
128
194
|
args.command = "help";
|
|
129
195
|
return args;
|
|
130
196
|
}
|
|
197
|
+
if (rest[0] === "-v" || rest[0] === "--version") {
|
|
198
|
+
args.command = "version";
|
|
199
|
+
return args;
|
|
200
|
+
}
|
|
201
|
+
if (rest[0] === "dispatch" && (rest[1] === "-h" || rest[1] === "--help")) {
|
|
202
|
+
args.command = "dispatch";
|
|
203
|
+
args.help = true;
|
|
204
|
+
return args;
|
|
205
|
+
}
|
|
131
206
|
args.command = rest[0];
|
|
132
207
|
for (let i = 1; i < rest.length; i++) {
|
|
133
208
|
const a = rest[i];
|
|
134
|
-
if (a === "--
|
|
135
|
-
else if (a === "--no-ai") args.ai = false;
|
|
209
|
+
if (a === "--no-ai") args.ai = false;
|
|
136
210
|
else if (a === "--example") args.example = true;
|
|
137
211
|
else if (a === "--cwd" && rest[i + 1]) {
|
|
138
212
|
args.cwdProvided = true;
|
|
@@ -144,6 +218,52 @@ function parseArgs(argv) {
|
|
|
144
218
|
args.port = rest[++i];
|
|
145
219
|
} else if (a.startsWith("--port=")) {
|
|
146
220
|
args.port = a.slice("--port=".length);
|
|
221
|
+
} else if (a === "--board" && rest[i + 1]) {
|
|
222
|
+
args.board = rest[++i];
|
|
223
|
+
} else if (a.startsWith("--board=")) {
|
|
224
|
+
args.board = a.slice("--board=".length);
|
|
225
|
+
} else if (a === "--ticket" && rest[i + 1]) {
|
|
226
|
+
args.ticket = rest[++i];
|
|
227
|
+
} else if (a.startsWith("--ticket=")) {
|
|
228
|
+
args.ticket = a.slice("--ticket=".length);
|
|
229
|
+
} else if (a === "--profile" && rest[i + 1]) {
|
|
230
|
+
args.profile = rest[++i];
|
|
231
|
+
} else if (a.startsWith("--profile=")) {
|
|
232
|
+
args.profile = a.slice("--profile=".length);
|
|
233
|
+
} else if (a === "--policy" && rest[i + 1]) {
|
|
234
|
+
args.policy = rest[++i];
|
|
235
|
+
} else if (a.startsWith("--policy=")) {
|
|
236
|
+
args.policy = a.slice("--policy=".length);
|
|
237
|
+
} else if (a === "--server-url" && rest[i + 1]) {
|
|
238
|
+
args.serverUrl = rest[++i];
|
|
239
|
+
} else if (a.startsWith("--server-url=")) {
|
|
240
|
+
args.serverUrl = a.slice("--server-url=".length);
|
|
241
|
+
} else if (a === "--remote") {
|
|
242
|
+
args.remote = true;
|
|
243
|
+
} else if (a === "--worktree") {
|
|
244
|
+
args.worktree = true;
|
|
245
|
+
} else if (a === "--worktree-path" && rest[i + 1]) {
|
|
246
|
+
args.worktreePath = resolve(rest[++i]);
|
|
247
|
+
} else if (a.startsWith("--worktree-path=")) {
|
|
248
|
+
args.worktreePath = resolve(a.slice("--worktree-path=".length));
|
|
249
|
+
} else if (a === "--branch" && rest[i + 1]) {
|
|
250
|
+
args.branch = rest[++i];
|
|
251
|
+
} else if (a.startsWith("--branch=")) {
|
|
252
|
+
args.branch = a.slice("--branch=".length);
|
|
253
|
+
} else if (a === "--hermes-bin" && rest[i + 1]) {
|
|
254
|
+
args.hermesBin = rest[++i];
|
|
255
|
+
} else if (a.startsWith("--hermes-bin=")) {
|
|
256
|
+
args.hermesBin = a.slice("--hermes-bin=".length);
|
|
257
|
+
} else if (a === "--no-spawn") {
|
|
258
|
+
args.noSpawn = true;
|
|
259
|
+
} else if (a === "--no-yolo") {
|
|
260
|
+
args.yolo = false;
|
|
261
|
+
} else if (a === "--yolo") {
|
|
262
|
+
args.yolo = true;
|
|
263
|
+
} else if (a === "--keep-handoff-file") {
|
|
264
|
+
args.keepHandoffFile = true;
|
|
265
|
+
} else if (a === "--force") {
|
|
266
|
+
args.force = true;
|
|
147
267
|
} else if (a === "--image" && rest[i + 1]) {
|
|
148
268
|
args.image = rest[++i];
|
|
149
269
|
} else if (a.startsWith("--image=")) {
|
|
@@ -162,11 +282,13 @@ function parseArgs(argv) {
|
|
|
162
282
|
args.detach = true;
|
|
163
283
|
} else if (a === "--foreground") {
|
|
164
284
|
args.detach = false;
|
|
285
|
+
args.foreground = true;
|
|
165
286
|
} else if (a === "--dry-run") {
|
|
166
287
|
args.dryRun = true;
|
|
167
288
|
} else {
|
|
168
289
|
console.error(`Unknown argument: ${a}`);
|
|
169
290
|
args.command = "help";
|
|
291
|
+
args.invalid = true;
|
|
170
292
|
return args;
|
|
171
293
|
}
|
|
172
294
|
}
|
|
@@ -187,15 +309,13 @@ async function runInit(options) {
|
|
|
187
309
|
if (!existsSync(skillSrc)) {
|
|
188
310
|
console.warn(`Warning: missing ${skillSrc} — skipped copying SKILL-ORBIT.md and AGENTS.md Orbit section.`);
|
|
189
311
|
} else {
|
|
190
|
-
|
|
312
|
+
const skillSync = syncSkillOrbitMd(projectRoot, skillSrc);
|
|
313
|
+
if (skillSync.status === "same_file") {
|
|
191
314
|
console.log("SKILL-ORBIT.md source is already the project copy — left unchanged.");
|
|
192
|
-
} else if (existsSync(skillDest) && !options.refreshAgentsMd) {
|
|
193
|
-
console.log(`SKILL-ORBIT.md already exists — left unchanged (use --refresh-agents-md to replace).`);
|
|
194
315
|
} else {
|
|
195
|
-
copyFileSync(skillSrc, skillDest);
|
|
196
316
|
console.log(`Wrote ${skillDest}`);
|
|
197
317
|
}
|
|
198
|
-
syncAgentsMd(projectRoot
|
|
318
|
+
syncAgentsMd(projectRoot);
|
|
199
319
|
}
|
|
200
320
|
|
|
201
321
|
enableAiIfEnabled(options, result.registryRow, core);
|
|
@@ -204,7 +324,7 @@ async function runInit(options) {
|
|
|
204
324
|
if (result.message && result.status === "skipped") {
|
|
205
325
|
console.log(result.message);
|
|
206
326
|
} else if (result.status === "created") {
|
|
207
|
-
console.log(`Initialized Orbit board at ${
|
|
327
|
+
console.log(`Initialized Orbit board at ${result.registryRow?.db_path ?? "(unknown path)"}`);
|
|
208
328
|
} else if (result.status === "repaired") {
|
|
209
329
|
console.log(result.message || "Linked existing board database to the registry.");
|
|
210
330
|
}
|
|
@@ -231,6 +351,7 @@ async function runReset(options) {
|
|
|
231
351
|
DATA_DIR,
|
|
232
352
|
backupBoardDatabase,
|
|
233
353
|
backupRegistry,
|
|
354
|
+
closeConnection,
|
|
234
355
|
createRegistrySchema,
|
|
235
356
|
deleteBoard,
|
|
236
357
|
getBoardByRepoPath,
|
|
@@ -247,23 +368,53 @@ async function runReset(options) {
|
|
|
247
368
|
const registryRow = getBoardByRepoPath(projectRoot);
|
|
248
369
|
|
|
249
370
|
if (registryRow && existsSync(registryRow.db_path)) {
|
|
250
|
-
|
|
371
|
+
const db = openConnection(registryRow.db_path);
|
|
372
|
+
try {
|
|
373
|
+
backupBoardDatabase(registryRow, db, "pre-cli-reset");
|
|
374
|
+
} catch (error) {
|
|
375
|
+
throw resetBusyError(error, projectRoot);
|
|
376
|
+
} finally {
|
|
377
|
+
closeConnection(registryRow.db_path);
|
|
378
|
+
}
|
|
251
379
|
}
|
|
252
380
|
if (registryRow) backupRegistry("pre-cli-reset");
|
|
253
381
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
382
|
+
try {
|
|
383
|
+
// Remove the board database. With central storage the db lives outside the
|
|
384
|
+
// project directory; with legacy in-repo storage it is inside orbitDir below.
|
|
385
|
+
const isCentralDb = registryRow && !registryRow.db_path.startsWith(projectRoot + "/");
|
|
386
|
+
if (isCentralDb && existsSync(registryRow.db_path)) {
|
|
387
|
+
const boardDir = dirname(registryRow.db_path);
|
|
388
|
+
rmSync(boardDir, { recursive: true, force: true });
|
|
389
|
+
removed.push(boardDir);
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
// Remove central dispatch-runs for this board.
|
|
393
|
+
if (registryRow) {
|
|
394
|
+
const dispatchRunsDir = join(DATA_DIR, "dispatch-runs", registryRow.slug);
|
|
395
|
+
if (existsSync(dispatchRunsDir)) {
|
|
396
|
+
rmSync(dispatchRunsDir, { recursive: true, force: true });
|
|
397
|
+
removed.push(dispatchRunsDir);
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
// Remove the in-repo .orbit/ directory (covers legacy db, residual dispatch-runs, etc.).
|
|
402
|
+
if (existsSync(orbitDir)) {
|
|
403
|
+
rmSync(orbitDir, { recursive: true, force: true });
|
|
404
|
+
removed.push(orbitDir);
|
|
405
|
+
}
|
|
406
|
+
if (existsSync(skillMd)) {
|
|
407
|
+
unlinkSync(skillMd);
|
|
408
|
+
removed.push(skillMd);
|
|
409
|
+
}
|
|
410
|
+
const agentsCleanup = removeOrbitAgentsSection(projectRoot);
|
|
411
|
+
if (agentsCleanup.removed) {
|
|
412
|
+
removed.push(`${agentsCleanup.path} Orbit section`);
|
|
413
|
+
} else if (!agentsCleanup.ok) {
|
|
414
|
+
console.warn(`Warning: skipped AGENTS.md cleanup (${agentsCleanup.reason}) in ${agentsCleanup.path}`);
|
|
415
|
+
}
|
|
416
|
+
} catch (error) {
|
|
417
|
+
throw resetBusyError(error, projectRoot);
|
|
267
418
|
}
|
|
268
419
|
|
|
269
420
|
if (registryRow) {
|
|
@@ -284,6 +435,7 @@ async function runServe(options) {
|
|
|
284
435
|
// captures it at module load. cwd defaults are equivalent for most users, but
|
|
285
436
|
// an explicit --cwd lets a global install target a board outside cwd.
|
|
286
437
|
process.env.PROJECT_ROOT = resolve(options.cwd);
|
|
438
|
+
process.env.ORBIT_SYNC_MANAGED_SKILL_ORBIT = "1";
|
|
287
439
|
if (options.port) process.env.PORT = String(options.port);
|
|
288
440
|
await import("../server.js");
|
|
289
441
|
}
|
|
@@ -385,6 +537,35 @@ async function runMcp(options) {
|
|
|
385
537
|
await import("../mcp-server.js");
|
|
386
538
|
}
|
|
387
539
|
|
|
540
|
+
async function runDispatch(options) {
|
|
541
|
+
if (options.help) {
|
|
542
|
+
printUsage();
|
|
543
|
+
return;
|
|
544
|
+
}
|
|
545
|
+
process.env.PROJECT_ROOT = resolve(options.cwd);
|
|
546
|
+
const { dispatchTicket } = await import("../core/dispatch.js");
|
|
547
|
+
const result = dispatchTicket(options);
|
|
548
|
+
if (result.dry_run) {
|
|
549
|
+
console.log(`Dry run: would dispatch ticket #${result.ticket.number} ${result.ticket.title}`);
|
|
550
|
+
console.log(`Board: ${result.board.slug}`);
|
|
551
|
+
console.log(`Profile: ${result.profile}`);
|
|
552
|
+
console.log(`Mode: ${result.no_spawn ? "prepare-only" : "spawn"}`);
|
|
553
|
+
console.log("No files, worktrees, ticket fields, comments, or agents were changed.");
|
|
554
|
+
return;
|
|
555
|
+
}
|
|
556
|
+
console.log(`Dispatch ${result.spawned ? "started" : "prepared"}: ${result.run_id}`);
|
|
557
|
+
console.log(`Board: ${result.board.slug}`);
|
|
558
|
+
console.log(`Ticket: #${result.ticket.number} ${result.ticket.title}`);
|
|
559
|
+
console.log(`Profile: ${result.profile}`);
|
|
560
|
+
console.log(`Policy: ${result.policy}`);
|
|
561
|
+
console.log(`Worktree: ${result.worktree_path}`);
|
|
562
|
+
if (result.branch) console.log(`Branch: ${result.branch}`);
|
|
563
|
+
if (result.pid) console.log(`PID: ${result.pid}`);
|
|
564
|
+
console.log("AI Written-Plan updated with the generated handoff.");
|
|
565
|
+
console.log("Run record comment added to the ticket.");
|
|
566
|
+
if (result.no_spawn) console.log("No agent spawned; ticket state left unchanged.");
|
|
567
|
+
}
|
|
568
|
+
|
|
388
569
|
async function main() {
|
|
389
570
|
const args = parseArgs(process.argv);
|
|
390
571
|
try {
|
|
@@ -398,9 +579,13 @@ async function main() {
|
|
|
398
579
|
await runDocker(args);
|
|
399
580
|
} else if (args.command === "mcp") {
|
|
400
581
|
await runMcp(args);
|
|
582
|
+
} else if (args.command === "dispatch") {
|
|
583
|
+
await runDispatch(args);
|
|
584
|
+
} else if (args.command === "version") {
|
|
585
|
+
printVersion();
|
|
401
586
|
} else {
|
|
402
587
|
printUsage();
|
|
403
|
-
process.exitCode = args.command === "help" ? 0 : 1;
|
|
588
|
+
process.exitCode = args.command === "help" && !args.invalid ? 0 : 1;
|
|
404
589
|
}
|
|
405
590
|
} catch (err) {
|
|
406
591
|
console.error(err?.message || err);
|
package/src/core/agent.js
CHANGED
|
@@ -112,9 +112,6 @@ export function readTicket(lookup, ctx) {
|
|
|
112
112
|
const innerBoard = boardById(db, ticket.board_id);
|
|
113
113
|
requireBoardAccess(actor, innerBoard);
|
|
114
114
|
|
|
115
|
-
const comments = db
|
|
116
|
-
.prepare("SELECT * FROM comments WHERE ticket_id = ? ORDER BY created_at ASC")
|
|
117
|
-
.all(ticket.id);
|
|
118
115
|
const labels = labelsForTicket(db, ticket.id);
|
|
119
116
|
|
|
120
117
|
return {
|
|
@@ -128,7 +125,6 @@ export function readTicket(lookup, ctx) {
|
|
|
128
125
|
priority: ticket.priority,
|
|
129
126
|
labels
|
|
130
127
|
},
|
|
131
|
-
comments,
|
|
132
128
|
board_manual: boardManual(innerBoard.id, ctx)
|
|
133
129
|
};
|
|
134
130
|
}
|
|
@@ -220,7 +216,6 @@ export function getContextPack(ticketId, ctx, depth = 1) {
|
|
|
220
216
|
const innerBoard = boardById(db, ticket.board_id);
|
|
221
217
|
requireBoardAccess(actor, innerBoard);
|
|
222
218
|
|
|
223
|
-
const comments = db.prepare("SELECT * FROM comments WHERE ticket_id = ? ORDER BY created_at ASC").all(ticketId);
|
|
224
219
|
const labels = labelsForTicket(db, ticketId);
|
|
225
220
|
const relations = relationRows(db, ticketId);
|
|
226
221
|
const blockers = unresolvedBlockers(db, ticketId);
|
|
@@ -236,16 +231,6 @@ export function getContextPack(ticketId, ctx, depth = 1) {
|
|
|
236
231
|
...childTickets,
|
|
237
232
|
...(parentTicket ? [parentTicket] : [])
|
|
238
233
|
]);
|
|
239
|
-
const relatedCommentRows =
|
|
240
|
-
depth > 0
|
|
241
|
-
? relatedTickets.flatMap((related) =>
|
|
242
|
-
db
|
|
243
|
-
.prepare("SELECT * FROM comments WHERE ticket_id = ? ORDER BY created_at DESC LIMIT 5")
|
|
244
|
-
.all(related.id)
|
|
245
|
-
.reverse()
|
|
246
|
-
.map((comment) => ({ ...comment, ticket_id: related.id, ticket_title: related.title }))
|
|
247
|
-
)
|
|
248
|
-
: [];
|
|
249
234
|
|
|
250
235
|
return {
|
|
251
236
|
ticket: {
|
|
@@ -254,7 +239,6 @@ export function getContextPack(ticketId, ctx, depth = 1) {
|
|
|
254
239
|
},
|
|
255
240
|
board: innerBoard,
|
|
256
241
|
board_manual: boardManual(innerBoard.id, ctx),
|
|
257
|
-
comments,
|
|
258
242
|
parent_ticket: parentTicket ? compactTicket(parentTicket) : null,
|
|
259
243
|
child_tickets: childTickets.map((child) => ({
|
|
260
244
|
...child,
|
|
@@ -262,7 +246,6 @@ export function getContextPack(ticketId, ctx, depth = 1) {
|
|
|
262
246
|
})),
|
|
263
247
|
relations,
|
|
264
248
|
blockers,
|
|
265
|
-
related_tickets: relatedTickets
|
|
266
|
-
related_comments: relatedCommentRows
|
|
249
|
+
related_tickets: relatedTickets
|
|
267
250
|
};
|
|
268
251
|
}
|
package/src/core/agents-md.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
1
|
+
import { copyFileSync, existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
2
|
import { resolve } from "node:path";
|
|
3
3
|
|
|
4
4
|
export const ORBIT_AGENTS_START = "<!-- ORBIT:AGENTS-START -->";
|
|
@@ -21,7 +21,19 @@ export function buildAgentsMd() {
|
|
|
21
21
|
${orbitAgentsSection()}`;
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
export function
|
|
24
|
+
export function syncSkillOrbitMd(projectRoot, skillSrc) {
|
|
25
|
+
const skillDest = resolve(projectRoot, "SKILL-ORBIT.md");
|
|
26
|
+
if (!existsSync(skillSrc)) {
|
|
27
|
+
return { ok: false, status: "missing_source", source: skillSrc, path: skillDest };
|
|
28
|
+
}
|
|
29
|
+
if (resolve(skillSrc) === skillDest) {
|
|
30
|
+
return { ok: true, status: "same_file", source: skillSrc, path: skillDest };
|
|
31
|
+
}
|
|
32
|
+
copyFileSync(skillSrc, skillDest);
|
|
33
|
+
return { ok: true, status: "written", source: skillSrc, path: skillDest };
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function syncAgentsMd(projectRoot, _options = {}, logger = console.log) {
|
|
25
37
|
const agentsPath = resolve(projectRoot, "AGENTS.md");
|
|
26
38
|
const section = orbitAgentsSection();
|
|
27
39
|
|
|
@@ -36,14 +48,7 @@ export function syncAgentsMd(projectRoot, options = {}, logger = console.log) {
|
|
|
36
48
|
const endIndex = current.indexOf(ORBIT_AGENTS_END);
|
|
37
49
|
|
|
38
50
|
if (startIndex !== -1 && endIndex !== -1 && endIndex > startIndex) {
|
|
39
|
-
|
|
40
|
-
logger("AGENTS.md already includes Orbit instructions — left unchanged (use --refresh-agents-md to replace).");
|
|
41
|
-
return;
|
|
42
|
-
}
|
|
43
|
-
const before = current.slice(0, startIndex).replace(/\s*$/, "\n\n");
|
|
44
|
-
const after = current.slice(endIndex + ORBIT_AGENTS_END.length).replace(/^\s*/, "");
|
|
45
|
-
writeFileSync(agentsPath, `${before}${section}${after}`, "utf8");
|
|
46
|
-
logger(`Refreshed Orbit instructions inside ${agentsPath}`);
|
|
51
|
+
logger("AGENTS.md already includes Orbit instructions — left unchanged.");
|
|
47
52
|
return;
|
|
48
53
|
}
|
|
49
54
|
|