@bridge_gpt/mcp-server 0.1.16 → 0.1.17

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.
@@ -89,7 +89,8 @@ export function getStartTicketsUsage() {
89
89
  " --terminal terminal|iterm Override the macOS terminal app (default: auto-detect via $TERM_PROGRAM); honored on macOS only",
90
90
  " --dry-run Print intended actions; create no worktrees, open no tabs",
91
91
  " --branch KEY=BRANCH Use BRANCH instead of feature/KEY for that ticket (repeatable)",
92
- " --no-refresh-main Skip 'git fetch origin main' + fast-forward of local main",
92
+ " --base-branch BRANCH Cut new worktrees from BRANCH and refresh origin/BRANCH (default: main)",
93
+ " --no-refresh-main Skip refresh of the configured base branch (default main); historical name retained for backward compatibility",
93
94
  " --max-parallel N Max worktrees to create concurrently (default: 3)",
94
95
  " -h, --help Show this help",
95
96
  "",
@@ -116,9 +117,11 @@ export function parseStartTicketsArgs(argv) {
116
117
  }
117
118
  let terminal;
118
119
  let dryRun = false;
120
+ let autoApprove = false;
119
121
  let refreshMain = true;
120
122
  let maxParallelRaw;
121
123
  let agentName = DEFAULT_AGENT_NAME;
124
+ let baseBranch = "main";
122
125
  const branchEntries = [];
123
126
  const keys = [];
124
127
  for (let i = 0; i < argv.length; i++) {
@@ -198,10 +201,36 @@ export function parseStartTicketsArgs(argv) {
198
201
  branchEntries.push(value);
199
202
  continue;
200
203
  }
204
+ if (arg === "--base-branch" || arg.startsWith("--base-branch=")) {
205
+ let value;
206
+ if (arg.startsWith("--base-branch=")) {
207
+ value = arg.slice("--base-branch=".length);
208
+ }
209
+ else {
210
+ // For the space-separated form, reject a following option token so we
211
+ // don't silently consume "--dry-run" (etc.) as the branch value.
212
+ const next = i + 1 < argv.length ? argv[i + 1] : undefined;
213
+ if (next === undefined || next.startsWith("-")) {
214
+ return { status: "error", message: "--base-branch requires a value (a branch name)." };
215
+ }
216
+ value = takeValue();
217
+ }
218
+ const trimmed = (value ?? "").trim();
219
+ const error = validateBranchName(trimmed);
220
+ if (error) {
221
+ return { status: "error", message: `Invalid --base-branch value: ${error}` };
222
+ }
223
+ baseBranch = trimmed;
224
+ continue;
225
+ }
201
226
  if (arg === "--dry-run") {
202
227
  dryRun = true;
203
228
  continue;
204
229
  }
230
+ if (arg === "--auto-approve") {
231
+ autoApprove = true;
232
+ continue;
233
+ }
205
234
  if (arg === "--no-refresh-main") {
206
235
  refreshMain = false;
207
236
  continue;
@@ -277,13 +306,15 @@ export function parseStartTicketsArgs(argv) {
277
306
  }
278
307
  return {
279
308
  status: "ok",
280
- options: { keys, terminal, dryRun, refreshMain, maxParallel, branchOverrides, agentName },
309
+ options: { keys, terminal, dryRun, autoApprove, refreshMain, maxParallel, branchOverrides, agentName, baseBranch },
281
310
  };
282
311
  }
283
312
  /** Returns an error string for an unsafe branch name, or null when valid. */
284
- function validateBranchName(branch) {
285
- if (branch.length === 0)
313
+ export function validateBranchName(branch) {
314
+ if (branch.trim().length === 0)
286
315
  return "branch name must not be empty.";
316
+ if (branch.length > 255)
317
+ return "branch name must be 255 characters or fewer.";
287
318
  if (branch.startsWith("-"))
288
319
  return "branch name must not start with '-'.";
289
320
  // Reject ASCII control characters (0x00-0x1F and 0x7F) without embedding
@@ -343,7 +374,7 @@ export function getDefaultSpawnTerminalTabForPlatform(platform) {
343
374
  * are always honored). Returns a structured error for unsupported platforms;
344
375
  * never throws.
345
376
  */
346
- export function resolveStartTicketsPlatformConfig(deps, agent) {
377
+ export function resolveStartTicketsPlatformConfig(deps, agent, autoApprove = false) {
347
378
  if (!isSupportedStartTicketsPlatform(deps.platform)) {
348
379
  return { ok: false, error: unsupportedPlatformMessage(deps.platform) };
349
380
  }
@@ -353,7 +384,7 @@ export function resolveStartTicketsPlatformConfig(deps, agent) {
353
384
  config: {
354
385
  platform,
355
386
  worktrunkBinary: resolveWorktrunkBinary(platform, deps.env),
356
- buildAgentShellCommand: (key, worktreePath) => buildAgentShellCommand(agent, key, worktreePath, platform),
387
+ buildAgentShellCommand: (key, worktreePath) => buildAgentShellCommand(agent, key, worktreePath, platform, autoApprove),
357
388
  spawnTerminalTab: deps.spawnTerminalTab,
358
389
  },
359
390
  };
@@ -522,84 +553,106 @@ export function parseGitWorktreeList(output) {
522
553
  entries.push(current);
523
554
  return entries;
524
555
  }
525
- /** Return the worktree path owning branch `main`, or null. */
526
- export function findMainWorktreePath(entries) {
556
+ /** Return the worktree path owning branch `baseBranch`, or null. */
557
+ export function findBaseWorktreePath(entries, baseBranch) {
527
558
  for (const entry of entries) {
528
- if (entry.branch === "main")
559
+ if (entry.branch === baseBranch)
529
560
  return entry.path;
530
561
  }
531
562
  return null;
532
563
  }
533
564
  /**
534
- * Fetch origin/main and fast-forward local `main`. No-op when `refreshMain` is
535
- * false. Handles both checkout states:
565
+ * Compatibility wrapper: returns the worktree path owning branch `main`, or
566
+ * null. Defers to {@link findBaseWorktreePath} so the `main` and non-main paths
567
+ * stay identical.
568
+ */
569
+ export function findMainWorktreePath(entries) {
570
+ return findBaseWorktreePath(entries, "main");
571
+ }
572
+ /**
573
+ * Fetch origin/<baseBranch> and fast-forward the local base branch. No-op when
574
+ * `refreshMain` is false. (The option key keeps its historical name so the
575
+ * `--no-refresh-main` user-facing flag remains backward-compatible; the
576
+ * behavior now follows whatever `baseBranch` resolves to, default `"main"`.)
536
577
  *
537
- * - `main` IS checked out in a worktree → `git merge --ff-only` inside that
538
- * worktree, so its index/working tree stay consistent (git forbids
578
+ * Handles both checkout states:
579
+ *
580
+ * - `<baseBranch>` IS checked out in a worktree → `git merge --ff-only` inside
581
+ * that worktree, so its index/working tree stay consistent (git forbids
539
582
  * force-moving a checked-out branch ref).
540
- * - `main` is NOT checked out anywhere (e.g. the primary checkout is on a
541
- * feature/chore branch) → fast-forward the ref directly with
583
+ * - `<baseBranch>` is NOT checked out anywhere (e.g. the primary checkout is
584
+ * on a feature/chore branch) → fast-forward the ref directly with
542
585
  * `git branch --force`, since no working tree depends on it. This is guarded
543
- * by an ancestry check so a diverged local `main` is never clobbered, and it
544
- * creates `main` from origin/main when no local ref exists yet.
586
+ * by an ancestry check so a diverged local base is never clobbered, and it
587
+ * creates the local base ref from origin when no local ref exists yet.
545
588
  *
546
589
  * Returns a structured failure for any expected problem (fetch failure, diverged
547
- * main, or a failed ref update).
590
+ * base branch, or a failed ref update).
548
591
  */
549
- export async function refreshMainBranch(deps, options) {
592
+ export async function refreshBaseBranch(deps, options) {
550
593
  if (!options.refreshMain)
551
594
  return { ok: true };
552
- const fetch = await deps.runCommand("git", ["fetch", "origin", "main"], {
595
+ const baseBranch = options.baseBranch;
596
+ const originRef = `origin/${baseBranch}`;
597
+ const fetch = await deps.runCommand("git", ["fetch", "origin", baseBranch], {
553
598
  cwd: deps.cwd,
554
599
  });
555
600
  if (!commandSucceeded(fetch)) {
556
601
  return {
557
602
  ok: false,
558
- error: "git fetch origin main failed. Check your network and 'git remote get-url origin', or pass --no-refresh-main to skip.",
603
+ error: `git fetch origin ${baseBranch} failed. Check your network and 'git remote get-url origin', or pass --no-refresh-main to skip.`,
559
604
  };
560
605
  }
561
606
  const list = await deps.runCommand("git", ["worktree", "list", "--porcelain"], { cwd: deps.cwd });
562
607
  if (!commandSucceeded(list)) {
563
608
  return {
564
609
  ok: false,
565
- error: "git worktree list --porcelain failed; cannot locate the main worktree.",
610
+ error: `git worktree list --porcelain failed; cannot locate the ${baseBranch} worktree.`,
566
611
  };
567
612
  }
568
- const mainPath = findMainWorktreePath(parseGitWorktreeList(list.stdout));
569
- // `main` is checked out somewhere: fast-forward it in place. git refuses to
613
+ const basePath = findBaseWorktreePath(parseGitWorktreeList(list.stdout), baseBranch);
614
+ // `<baseBranch>` is checked out somewhere: fast-forward it in place. git refuses to
570
615
  // force-move a checked-out branch ref, so we must go through merge.
571
- if (mainPath) {
572
- const merge = await deps.runCommand("git", ["merge", "--ff-only", "origin/main"], { cwd: mainPath });
616
+ if (basePath) {
617
+ const merge = await deps.runCommand("git", ["merge", "--ff-only", originRef], { cwd: basePath });
573
618
  if (!commandSucceeded(merge)) {
574
619
  return {
575
620
  ok: false,
576
- error: `Local main has diverged from origin/main (checked out at ${mainPath}). Resolve the divergence manually, or rerun with --no-refresh-main.`,
621
+ error: `Local ${baseBranch} has diverged from ${originRef} (checked out at ${basePath}). Resolve the divergence manually, or rerun with --no-refresh-main.`,
577
622
  };
578
623
  }
579
624
  return { ok: true };
580
625
  }
581
- // `main` is not checked out in any worktree. No working tree depends on the
582
- // ref, so fast-forward it directly — but only when it is a true fast-forward,
583
- // never clobbering local-only commits. When local `main` does not exist yet,
584
- // create it from origin/main.
585
- if (await branchExists(deps, "main")) {
586
- const ancestor = await deps.runCommand("git", ["merge-base", "--is-ancestor", "main", "origin/main"], { cwd: deps.cwd });
626
+ // `<baseBranch>` is not checked out in any worktree. No working tree depends
627
+ // on the ref, so fast-forward it directly — but only when it is a true
628
+ // fast-forward, never clobbering local-only commits. When the local base ref
629
+ // does not exist yet, create it from origin.
630
+ if (await branchExists(deps, baseBranch)) {
631
+ const ancestor = await deps.runCommand("git", ["merge-base", "--is-ancestor", baseBranch, originRef], { cwd: deps.cwd });
587
632
  if (!commandSucceeded(ancestor)) {
588
633
  return {
589
634
  ok: false,
590
- error: "Local main has diverged from origin/main. Resolve the divergence manually, or rerun with --no-refresh-main.",
635
+ error: `Local ${baseBranch} has diverged from ${originRef}. Resolve the divergence manually, or rerun with --no-refresh-main.`,
591
636
  };
592
637
  }
593
638
  }
594
- const update = await deps.runCommand("git", ["branch", "--force", "main", "origin/main"], { cwd: deps.cwd });
639
+ const update = await deps.runCommand("git", ["branch", "--force", baseBranch, originRef], { cwd: deps.cwd });
595
640
  if (!commandSucceeded(update)) {
596
641
  return {
597
642
  ok: false,
598
- error: "Failed to fast-forward local main to origin/main. Resolve manually, or rerun with --no-refresh-main.",
643
+ error: `Failed to fast-forward local ${baseBranch} to ${originRef}. Resolve manually, or rerun with --no-refresh-main.`,
599
644
  };
600
645
  }
601
646
  return { ok: true };
602
647
  }
648
+ /**
649
+ * Compatibility wrapper around {@link refreshBaseBranch} that preserves the
650
+ * historical `refreshMainBranch(deps, { refreshMain })` signature for callers
651
+ * that target `main` specifically.
652
+ */
653
+ export async function refreshMainBranch(deps, options) {
654
+ return refreshBaseBranch(deps, { refreshMain: options.refreshMain, baseBranch: "main" });
655
+ }
603
656
  // ---------------------------------------------------------------------------
604
657
  // Concurrency + worktree creation
605
658
  // ---------------------------------------------------------------------------
@@ -643,11 +696,11 @@ export async function branchExists(deps, branch) {
643
696
  * tab spawning separately. The args are platform-agnostic; only the binary
644
697
  * differs (`wt` vs `git-wt`).
645
698
  */
646
- export function buildWtSwitchArgs(branch, exists) {
699
+ export function buildWtSwitchArgs(branch, exists, baseBranch = "main") {
647
700
  if (exists) {
648
701
  return ["switch", "-y", branch, "--format=json"];
649
702
  }
650
- return ["switch", "--create", "-y", branch, "--format=json"];
703
+ return ["switch", "--create", "-y", branch, "-b", baseBranch, "--format=json"];
651
704
  }
652
705
  /** Return the Node `path` API matching a platform (`win32` vs POSIX). */
653
706
  export function pathApiForPlatform(platform) {
@@ -698,11 +751,11 @@ function pickWorktreePathField(parsed) {
698
751
  * success (with key, branch, path) or a `create-failed` row on any expected
699
752
  * failure — never throws for per-ticket problems.
700
753
  */
701
- export async function createWorktreeForTicket(deps, key, branchOverrides, worktrunkBinary) {
754
+ export async function createWorktreeForTicket(deps, key, branchOverrides, worktrunkBinary, baseBranch = "main") {
702
755
  const branch = resolveBranchForTicket(key, branchOverrides);
703
756
  try {
704
757
  const exists = await branchExists(deps, branch);
705
- const args = buildWtSwitchArgs(branch, exists);
758
+ const args = buildWtSwitchArgs(branch, exists, baseBranch);
706
759
  const result = await deps.runCommand(worktrunkBinary, args, { cwd: deps.cwd });
707
760
  if (!commandSucceeded(result)) {
708
761
  const reason = (result.stderr || result.stdout || "").trim();
@@ -728,14 +781,18 @@ export async function createWorktreeForTicket(deps, key, branchOverrides, worktr
728
781
  * aborting the run.
729
782
  */
730
783
  export async function createWorktrees(deps, options, worktrunkBinary) {
731
- return runWithConcurrency(options.keys, options.maxParallel, (key) => createWorktreeForTicket(deps, key, options.branchOverrides, worktrunkBinary));
784
+ return runWithConcurrency(options.keys, options.maxParallel, (key) => createWorktreeForTicket(deps, key, options.branchOverrides, worktrunkBinary, options.baseBranch));
732
785
  }
733
786
  // ---------------------------------------------------------------------------
734
787
  // Per-platform shell-command construction
735
788
  // ---------------------------------------------------------------------------
736
- /** The starter prompt handed to the selected agent. Identical for every agent. */
737
- export function buildAgentPrompt(key) {
738
- return `/implement-ticket ${key}`;
789
+ /**
790
+ * The starter prompt handed to the selected agent. Identical for every agent.
791
+ * When `autoApprove` is set, the implementation agent runs hands-off
792
+ * (`/implement-ticket <KEY> --auto-approve`) — used by full-automation chains.
793
+ */
794
+ export function buildAgentPrompt(key, opts = {}) {
795
+ return `/implement-ticket ${key}${opts.autoApprove ? " --auto-approve" : ""}`;
739
796
  }
740
797
  /**
741
798
  * Build the agent invocation (`<command> <quotedPrompt>`) for the agent's prompt
@@ -754,13 +811,13 @@ export function buildAgentInvocation(agent, prompt, quote) {
754
811
  }
755
812
  }
756
813
  /** POSIX agent shell command: `cd '<path>' && <agent> '<prompt>'`. */
757
- export function buildPosixAgentShellCommand(agent, key, worktreePath) {
758
- const invocation = buildAgentInvocation(agent, buildAgentPrompt(key), (p) => `'${shSquoteInner(p)}'`);
814
+ export function buildPosixAgentShellCommand(agent, key, worktreePath, autoApprove = false) {
815
+ const invocation = buildAgentInvocation(agent, buildAgentPrompt(key, { autoApprove }), (p) => `'${shSquoteInner(p)}'`);
759
816
  return `cd '${shSquoteInner(worktreePath)}' && ${invocation}`;
760
817
  }
761
818
  /** PowerShell agent shell command: `Set-Location -LiteralPath '<path>'; <agent> '<prompt>'`. */
762
- export function buildPowerShellAgentShellCommand(agent, key, worktreePath) {
763
- const invocation = buildAgentInvocation(agent, buildAgentPrompt(key), powershellSquote);
819
+ export function buildPowerShellAgentShellCommand(agent, key, worktreePath, autoApprove = false) {
820
+ const invocation = buildAgentInvocation(agent, buildAgentPrompt(key, { autoApprove }), powershellSquote);
764
821
  return `Set-Location -LiteralPath ${powershellSquote(worktreePath)}; ${invocation}`;
765
822
  }
766
823
  /**
@@ -769,10 +826,10 @@ export function buildPowerShellAgentShellCommand(agent, key, worktreePath) {
769
826
  * dry-run fallback). The selected `agent` (never a module-level constant)
770
827
  * determines the launched command.
771
828
  */
772
- export function buildAgentShellCommand(agent, key, worktreePath, platform = "darwin") {
829
+ export function buildAgentShellCommand(agent, key, worktreePath, platform = "darwin", autoApprove = false) {
773
830
  if (platform === "win32")
774
- return buildPowerShellAgentShellCommand(agent, key, worktreePath);
775
- return buildPosixAgentShellCommand(agent, key, worktreePath);
831
+ return buildPowerShellAgentShellCommand(agent, key, worktreePath, autoApprove);
832
+ return buildPosixAgentShellCommand(agent, key, worktreePath, autoApprove);
776
833
  }
777
834
  // ---------------------------------------------------------------------------
778
835
  // macOS terminal spawning (behind the injected boundary)
@@ -1048,10 +1105,10 @@ export function buildDryRunResults(keys, overrides) {
1048
1105
  * PowerShell on Windows, `wt` + POSIX on macOS/Linux, and a non-throwing `wt` +
1049
1106
  * POSIX fallback for unsupported platforms.
1050
1107
  */
1051
- export function getDryRunPlatformDetails(agent, platform = process.platform, env = process.env) {
1108
+ export function getDryRunPlatformDetails(agent, platform = process.platform, env = process.env, autoApprove = false) {
1052
1109
  return {
1053
1110
  worktrunkBinary: resolveWorktrunkBinary(platform, env),
1054
- buildAgentShellCommand: (key, worktreePath) => buildAgentShellCommand(agent, key, worktreePath, platform),
1111
+ buildAgentShellCommand: (key, worktreePath) => buildAgentShellCommand(agent, key, worktreePath, platform, autoApprove),
1055
1112
  };
1056
1113
  }
1057
1114
  /**
@@ -1059,9 +1116,9 @@ export function getDryRunPlatformDetails(agent, platform = process.platform, env
1059
1116
  * platform-correct Worktrunk binary and the selected agent's shell command. Pure
1060
1117
  * platform formatting only — no preflight, no routing failures.
1061
1118
  */
1062
- export function buildDryRunDetailLines(agent, key, branch, platform = process.platform, env = process.env) {
1063
- const { worktrunkBinary, buildAgentShellCommand: build } = getDryRunPlatformDetails(agent, platform, env);
1064
- const wtArgs = buildWtSwitchArgs(branch, false);
1119
+ export function buildDryRunDetailLines(agent, key, branch, platform = process.platform, env = process.env, baseBranch = "main", autoApprove = false) {
1120
+ const { worktrunkBinary, buildAgentShellCommand: build } = getDryRunPlatformDetails(agent, platform, env, autoApprove);
1121
+ const wtArgs = buildWtSwitchArgs(branch, false, baseBranch);
1065
1122
  const agentInvocation = build(key, "<worktree-path>");
1066
1123
  return [
1067
1124
  `DRY-RUN: ${key} -> branch=${branch}`,
@@ -1117,10 +1174,13 @@ export async function orchestrateStartTickets(deps, options) {
1117
1174
  const preflight = await runPreflight(deps, options);
1118
1175
  if (!preflight.ok)
1119
1176
  return { ok: false, error: preflight.error };
1120
- const platformConfig = resolveStartTicketsPlatformConfig(deps, agent);
1177
+ const platformConfig = resolveStartTicketsPlatformConfig(deps, agent, options.autoApprove);
1121
1178
  if (!platformConfig.ok)
1122
1179
  return { ok: false, error: platformConfig.error };
1123
- const refresh = await refreshMainBranch(deps, options);
1180
+ const refresh = await refreshBaseBranch(deps, {
1181
+ refreshMain: options.refreshMain,
1182
+ baseBranch: options.baseBranch,
1183
+ });
1124
1184
  if (!refresh.ok)
1125
1185
  return { ok: false, error: refresh.error };
1126
1186
  const created = await createWorktrees(deps, options, platformConfig.config.worktrunkBinary);
@@ -1179,7 +1239,7 @@ export async function runStartTicketsCli(argv, overrides = {}) {
1179
1239
  if (options.dryRun) {
1180
1240
  for (const key of options.keys) {
1181
1241
  const branch = resolveBranchForTicket(key, options.branchOverrides);
1182
- for (const line of buildDryRunDetailLines(agent, key, branch, deps.platform, deps.env)) {
1242
+ for (const line of buildDryRunDetailLines(agent, key, branch, deps.platform, deps.env, options.baseBranch, options.autoApprove)) {
1183
1243
  log(line);
1184
1244
  }
1185
1245
  }
@@ -1,2 +1,2 @@
1
1
  // AUTO-GENERATED — do not edit manually. Regenerate with: npm run build
2
- export const VERSION = "0.1.16";
2
+ export const VERSION = "0.1.17";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bridge_gpt/mcp-server",
3
- "version": "0.1.16",
3
+ "version": "0.1.17",
4
4
  "description": "Bridge API MCP server — exposes Jira endpoints as MCP tools for Claude Code agents",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -12,6 +12,7 @@
12
12
  "!build/**/*.test.js",
13
13
  "!build/integration/**",
14
14
  "pipelines/",
15
+ "smoke-test/",
15
16
  "README.md",
16
17
  "LICENSE"
17
18
  ],
@@ -19,18 +20,17 @@
19
20
  "build": "node scripts/bundle-version.js && node scripts/bundle-pipelines.js && node scripts/bundle-commands.js && node scripts/bundle-agents.js && tsc",
20
21
  "postbuild": "node scripts/prepend-shebang.cjs",
21
22
  "start": "node build/index.js",
22
- "test": "node --test build/pipeline-utils.test.js build/update-check.test.js build/cli-upgrade.test.js build/decision-page-schema.test.js build/decision-page-template.test.js build/bundle-pipelines.test.js build/instructions-contract.test.js build/pipeline-orchestrator-persistence.test.js build/pipeline-orchestrator-execution.test.js build/pipeline-orchestrator-integration.test.js build/index-static.test.js build/start-tickets.test.js build/agent-registry.test.js build/start-tickets-prereqs.test.js build/doctor.test.js build/package-static.test.js",
23
+ "test": "node --test build/pipeline-utils.test.js build/update-check.test.js build/cli-upgrade.test.js build/decision-page-schema.test.js build/decision-page-template.test.js build/bundle-pipelines.test.js build/instructions-contract.test.js build/pipeline-orchestrator-persistence.test.js build/pipeline-orchestrator-execution.test.js build/pipeline-orchestrator-integration.test.js build/index-static.test.js build/start-tickets.test.js build/start-tickets-base-branch.test.js build/agent-registry.test.js build/start-tickets-prereqs.test.js build/doctor.test.js build/package-static.test.js build/chain-utils.test.js build/chain-orchestrator.test.js build/scheduler-backends/types.test.js build/scheduler-backends/escaping.test.js build/scheduler-backends/launchd.test.js build/scheduler-backends/task-scheduler.test.js build/scheduler-backends/systemd-user.test.js build/scheduler-backends/at-fallback.test.js build/scheduler-backends/index.test.js build/agent-launchers/claude.test.js build/agent-launchers/index.test.js build/schedule-store.test.js build/schedule-run.test.js",
23
24
  "test:integration": "node --test build/integration/refresh-main.integration.test.js build/integration/start-tickets.integration.test.js build/integration/doctor.integration.test.js",
24
25
  "prepublishOnly": "npm run build && node scripts/verify-shebang.cjs"
25
26
  },
26
27
  "dependencies": {
27
- "@modelcontextprotocol/sdk": "^1.26.0",
28
- "zod": "^3.25.0"
28
+ "@modelcontextprotocol/sdk": "^1.29.0",
29
+ "zod": "^4.4.3"
29
30
  },
30
31
  "devDependencies": {
31
- "@types/node": "^22.0.0",
32
- "typescript": "^5.7.0",
33
- "undici": "^6.25.0"
32
+ "@types/node": "^25.9.1",
33
+ "typescript": "^6.0.3"
34
34
  },
35
35
  "engines": {
36
36
  "node": ">=18.0.0"
@@ -0,0 +1,47 @@
1
+ {
2
+ "name": "full-automation",
3
+ "description": "Drive an idea end-to-end: create ticket(s) (idea-to-ticket), review each ticket (review-ticket fan-out), then hand off to the /start-tickets CLI seam to spawn worktrees.",
4
+ "variables": [
5
+ "idea",
6
+ "auto_approve",
7
+ "scheduled_at",
8
+ "max_children",
9
+ "allow_duplicate",
10
+ "agent"
11
+ ],
12
+ "stages": [
13
+ {
14
+ "pipeline_name": "idea-to-ticket",
15
+ "description": "Convert the idea into one or more Jira tickets.",
16
+ "variables": {
17
+ "idea": "{idea}",
18
+ "max_children": "{max_children}",
19
+ "allow_duplicate": "{allow_duplicate}",
20
+ "auto_approve_external": "{auto_approve}"
21
+ },
22
+ "outputs": {
23
+ "created_ticket_keys": "created_ticket_keys"
24
+ }
25
+ },
26
+ {
27
+ "pipeline_name": "review-ticket",
28
+ "description": "Review each created ticket (one child pipeline run per ticket).",
29
+ "fan_out_input": "created_ticket_keys",
30
+ "fan_out_variable": "ticket_key",
31
+ "variables": {
32
+ "ticket_key": "{ticket_key}"
33
+ },
34
+ "outputs": {
35
+ "reviewed_ticket_keys": "reviewed_ticket_keys"
36
+ }
37
+ },
38
+ {
39
+ "pipeline_name": "start-tickets",
40
+ "description": "Hand off the reviewed tickets to the /start-tickets CLI seam (agent-task; not a server pipeline).",
41
+ "fan_out_input": "reviewed_ticket_keys",
42
+ "outputs": {
43
+ "started_ticket_keys": "started_ticket_keys"
44
+ }
45
+ }
46
+ ]
47
+ }
@@ -0,0 +1,71 @@
1
+ {
2
+ "name": "Idea to Ticket",
3
+ "description": "Convert a short human idea into either a single Jira Task/Spike or a Jira Epic plus decomposed child tickets.",
4
+ "variables": [
5
+ "idea",
6
+ "idea_hash",
7
+ "slug",
8
+ "run_id",
9
+ "docs_dir",
10
+ "allow_duplicate",
11
+ "auto_approve_external",
12
+ "max_children"
13
+ ],
14
+ "steps": [
15
+ {
16
+ "type": "mcp_call",
17
+ "tool": "ping",
18
+ "params": {},
19
+ "description": "Verify Bridge API connectivity"
20
+ },
21
+ {
22
+ "type": "mcp_call",
23
+ "tool": "get_docs_dir",
24
+ "params": {},
25
+ "description": "Resolve the docs directory for run artifacts"
26
+ },
27
+ {
28
+ "type": "mcp_call",
29
+ "tool": "get_project_standards",
30
+ "params": {},
31
+ "description": "Fetch project standards for screening (optional)",
32
+ "on_error": "warn_and_continue"
33
+ },
34
+ {
35
+ "type": "agent_task",
36
+ "instruction_file": "preflight-and-readiness.md",
37
+ "description": "Initialize run directory, classify readiness and scope"
38
+ },
39
+ {
40
+ "type": "agent_task",
41
+ "instruction_file": "research-decision.md",
42
+ "description": "Decide which research tools to run for this idea"
43
+ },
44
+ {
45
+ "type": "agent_task",
46
+ "instruction_file": "execute-research.md",
47
+ "description": "Execute the planned research and produce the research pack",
48
+ "on_error": "warn_and_continue"
49
+ },
50
+ {
51
+ "type": "agent_task",
52
+ "instruction_file": "duplicate-and-context-scan.md",
53
+ "description": "Scan Jira for duplicate/related tickets"
54
+ },
55
+ {
56
+ "type": "agent_task",
57
+ "instruction_file": "screen-and-resolve.md",
58
+ "description": "Build standards checklist and resolve open questions"
59
+ },
60
+ {
61
+ "type": "agent_task",
62
+ "instruction_file": "draft-and-critique.md",
63
+ "description": "Draft the ticket(s) and run a hygiene/critique pass"
64
+ },
65
+ {
66
+ "type": "agent_task",
67
+ "instruction_file": "upload-and-track.md",
68
+ "description": "Upload to Jira and track the new ticket(s) idempotently"
69
+ }
70
+ ]
71
+ }