@bridge_gpt/mcp-server 0.1.9 → 0.1.11

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 CHANGED
@@ -21,6 +21,18 @@ npx -y @bridge_gpt/mcp-server --init
21
21
 
22
22
  Re-run `--init` after upgrading the package to get updated commands.
23
23
 
24
+ ### Upgrading
25
+
26
+ To upgrade to the latest version and refresh all scaffolded artifacts in one step:
27
+
28
+ ```bash
29
+ npx -y @bridge_gpt/mcp-server --upgrade
30
+ ```
31
+
32
+ This runs `npm i @bridge_gpt/mcp-server@latest`, prints a before/after version summary, then re-runs the full `--init` scaffolding flow to update your slash commands, agents, and pipeline definitions.
33
+
34
+ The MCP server also checks for updates automatically on startup. If a newer version is available, you'll see a notice in your editor's MCP output logs with the upgrade command to run. This check is cached for 24 hours and never blocks server startup.
35
+
24
36
  ### 2. Generate an API Key
25
37
 
26
38
  1. Log in to [Bridge API](https://bridgegpt-api.com) and navigate to your project's **Security** page
package/build/index.js CHANGED
@@ -14,9 +14,13 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
14
14
  import { z } from "zod";
15
15
  import { writeFile, mkdir, readFile, stat } from "fs/promises";
16
16
  import path from "path";
17
+ import { execSync } from "child_process";
18
+ import { createRequire } from "module";
17
19
  import { PIPELINES as BUNDLED_PIPELINES, INSTRUCTIONS as BUNDLED_INSTRUCTIONS } from "./pipelines.generated.js";
18
20
  import { COMMANDS } from "./commands.generated.js";
19
21
  import { AGENTS } from "./agents.generated.js";
22
+ import { VERSION } from "./version.generated.js";
23
+ import { checkForUpdate } from "./update-check.js";
20
24
  import { reconstructAgentMarkdown, translateAgentToCopilot } from "./agent-utils.js";
21
25
  import { resolveRecipe, loadCustomPipelines } from "./pipeline-utils.js";
22
26
  import { generateDecisionPageHtml } from "./decision-page-template.js";
@@ -239,174 +243,187 @@ async function ensureGitignored(cwd, filePath) {
239
243
  const separator = content.length > 0 && !content.endsWith("\n") ? "\n" : "";
240
244
  await writeFile(gitignorePath, content + separator + entry + "\n", "utf-8");
241
245
  }
242
- if (process.argv.includes("--init")) {
243
- // Guard: must be run from project root
246
+ /**
247
+ * Core initialization logic shared by --init and --upgrade.
248
+ * Runs all scaffolding phases without calling process.exit().
249
+ */
250
+ async function runInit(cwd) {
251
+ // ---- Phase 1: IDE Detection ----
252
+ const ideDetection = {
253
+ claude: true,
254
+ vscode: false,
255
+ cursor: false,
256
+ windsurf: false,
257
+ };
244
258
  try {
245
- await stat(path.join(process.cwd(), "package.json"));
259
+ await stat(path.join(cwd, ".vscode"));
260
+ ideDetection.vscode = true;
246
261
  }
247
- catch {
248
- console.error("Error: No package.json found in current directory.\n" +
249
- "--init must be run from your project root (the directory containing package.json).");
250
- process.exit(1);
262
+ catch { }
263
+ try {
264
+ await stat(path.join(cwd, ".cursor"));
265
+ ideDetection.cursor = true;
251
266
  }
267
+ catch { }
268
+ if (!ideDetection.cursor && process.env.CURSOR_TRACE_DIR)
269
+ ideDetection.cursor = true;
252
270
  try {
253
- const cwd = process.cwd();
254
- // ---- Phase 1: IDE Detection ----
255
- const ideDetection = {
256
- claude: true,
257
- vscode: false,
258
- cursor: false,
259
- windsurf: false,
260
- };
271
+ await stat(path.join(cwd, ".windsurf"));
272
+ ideDetection.windsurf = true;
273
+ }
274
+ catch { }
275
+ if (!ideDetection.windsurf) {
261
276
  try {
262
- await stat(path.join(cwd, ".vscode"));
263
- ideDetection.vscode = true;
277
+ await stat(path.join(cwd, ".windsurfrules"));
278
+ ideDetection.windsurf = true;
264
279
  }
265
280
  catch { }
281
+ }
282
+ const detectedIDEs = Object.entries(ideDetection)
283
+ .filter(([, v]) => v)
284
+ .map(([k]) => k);
285
+ console.log(`Bridge API --init: detected IDEs: ${detectedIDEs.join(", ")}`);
286
+ // ---- Phase 2: Windsurf manual instructions ----
287
+ if (ideDetection.windsurf) {
288
+ const windsurfSnippet = JSON.stringify({ mcpServers: { "bridge-api": buildBridgeApiEntry(cwd) } }, null, 2);
289
+ console.log("\n⚠ Windsurf does not support project-local MCP configuration.\n" +
290
+ "Add the following to ~/.codeium/windsurf/mcp_config.json:\n\n" +
291
+ windsurfSnippet + "\n");
292
+ }
293
+ // ---- Phase 3: Config file handling ----
294
+ const configTargets = [
295
+ { path: ".mcp.json", topLevelKey: "mcpServers", shouldCreate: true },
296
+ { path: ".vscode/mcp.json", topLevelKey: "servers", shouldCreate: ideDetection.vscode },
297
+ { path: ".cursor/mcp.json", topLevelKey: "mcpServers", shouldCreate: ideDetection.cursor },
298
+ ];
299
+ const configActions = [];
300
+ let anyCreatedOrAdded = false;
301
+ for (const target of configTargets) {
302
+ const fullPath = path.join(cwd, target.path);
303
+ let fileExists = false;
266
304
  try {
267
- await stat(path.join(cwd, ".cursor"));
268
- ideDetection.cursor = true;
305
+ await stat(fullPath);
306
+ fileExists = true;
269
307
  }
270
308
  catch { }
271
- if (!ideDetection.cursor && process.env.CURSOR_TRACE_DIR)
272
- ideDetection.cursor = true;
273
- try {
274
- await stat(path.join(cwd, ".windsurf"));
275
- ideDetection.windsurf = true;
309
+ if (!target.shouldCreate && !fileExists) {
310
+ configActions.push({ path: target.path, action: "skipped — IDE not detected" });
311
+ continue;
276
312
  }
277
- catch { }
278
- if (!ideDetection.windsurf) {
313
+ if (fileExists) {
314
+ // Read and parse existing file
315
+ const raw = await readFile(fullPath, "utf-8");
316
+ let parsed;
279
317
  try {
280
- await stat(path.join(cwd, ".windsurfrules"));
281
- ideDetection.windsurf = true;
318
+ parsed = JSON.parse(raw);
282
319
  }
283
- catch { }
284
- }
285
- const detectedIDEs = Object.entries(ideDetection)
286
- .filter(([, v]) => v)
287
- .map(([k]) => k);
288
- console.log(`Bridge API --init: detected IDEs: ${detectedIDEs.join(", ")}`);
289
- // ---- Phase 2: Windsurf manual instructions ----
290
- if (ideDetection.windsurf) {
291
- const windsurfSnippet = JSON.stringify({ mcpServers: { "bridge-api": buildBridgeApiEntry(cwd) } }, null, 2);
292
- console.log("\n⚠ Windsurf does not support project-local MCP configuration.\n" +
293
- "Add the following to ~/.codeium/windsurf/mcp_config.json:\n\n" +
294
- windsurfSnippet + "\n");
295
- }
296
- // ---- Phase 3: Config file handling ----
297
- const configTargets = [
298
- { path: ".mcp.json", topLevelKey: "mcpServers", shouldCreate: true },
299
- { path: ".vscode/mcp.json", topLevelKey: "servers", shouldCreate: ideDetection.vscode },
300
- { path: ".cursor/mcp.json", topLevelKey: "mcpServers", shouldCreate: ideDetection.cursor },
301
- ];
302
- const configActions = [];
303
- let anyCreatedOrAdded = false;
304
- for (const target of configTargets) {
305
- const fullPath = path.join(cwd, target.path);
306
- let fileExists = false;
307
- try {
308
- await stat(fullPath);
309
- fileExists = true;
310
- }
311
- catch { }
312
- if (!target.shouldCreate && !fileExists) {
313
- configActions.push({ path: target.path, action: "skipped — IDE not detected" });
320
+ catch {
321
+ console.warn(` ${target.path} skipped — invalid JSON format`);
322
+ configActions.push({ path: target.path, action: "skipped — invalid JSON" });
314
323
  continue;
315
324
  }
316
- if (fileExists) {
317
- // Read and parse existing file
318
- const raw = await readFile(fullPath, "utf-8");
319
- let parsed;
320
- try {
321
- parsed = JSON.parse(raw);
322
- }
323
- catch {
324
- console.warn(` ${target.path} skipped — invalid JSON format`);
325
- configActions.push({ path: target.path, action: "skipped — invalid JSON" });
326
- continue;
327
- }
328
- const topLevel = parsed[target.topLevelKey];
329
- if (topLevel && topLevel["bridge-api"]) {
330
- // Entry exists — update BAPI_PROJECT_ROOT only
331
- if (!topLevel["bridge-api"].env)
332
- topLevel["bridge-api"].env = {};
333
- topLevel["bridge-api"].env.BAPI_PROJECT_ROOT = cwd;
334
- await writeFile(fullPath, JSON.stringify(parsed, null, 2) + "\n", "utf-8");
335
- configActions.push({ path: target.path, action: "updated BAPI_PROJECT_ROOT" });
336
- }
337
- else {
338
- // Entry missing — add it, preserving existing content
339
- if (!parsed[target.topLevelKey])
340
- parsed[target.topLevelKey] = {};
341
- parsed[target.topLevelKey]["bridge-api"] = buildBridgeApiEntry(cwd);
342
- await writeFile(fullPath, JSON.stringify(parsed, null, 2) + "\n", "utf-8");
343
- configActions.push({ path: target.path, action: "added entry" });
344
- anyCreatedOrAdded = true;
345
- }
325
+ const topLevel = parsed[target.topLevelKey];
326
+ if (topLevel && topLevel["bridge-api"]) {
327
+ // Entry exists update BAPI_PROJECT_ROOT only
328
+ if (!topLevel["bridge-api"].env)
329
+ topLevel["bridge-api"].env = {};
330
+ topLevel["bridge-api"].env.BAPI_PROJECT_ROOT = cwd;
331
+ await writeFile(fullPath, JSON.stringify(parsed, null, 2) + "\n", "utf-8");
332
+ configActions.push({ path: target.path, action: "updated BAPI_PROJECT_ROOT" });
346
333
  }
347
334
  else {
348
- // Create new file
349
- await mkdir(path.dirname(fullPath), { recursive: true });
350
- const content = { [target.topLevelKey]: { "bridge-api": buildBridgeApiEntry(cwd) } };
351
- await writeFile(fullPath, JSON.stringify(content, null, 2) + "\n", "utf-8");
352
- await ensureGitignored(cwd, target.path);
353
- configActions.push({ path: target.path, action: "created" });
335
+ // Entry missing — add it, preserving existing content
336
+ if (!parsed[target.topLevelKey])
337
+ parsed[target.topLevelKey] = {};
338
+ parsed[target.topLevelKey]["bridge-api"] = buildBridgeApiEntry(cwd);
339
+ await writeFile(fullPath, JSON.stringify(parsed, null, 2) + "\n", "utf-8");
340
+ configActions.push({ path: target.path, action: "added entry" });
354
341
  anyCreatedOrAdded = true;
355
342
  }
356
343
  }
357
- console.log("\nMCP config files:");
358
- for (const entry of configActions) {
359
- console.log(` ${entry.path}: ${entry.action}`);
360
- }
361
- // ---- Phase 4: Scaffold slash command directories ----
362
- const commandDirs = [path.join(cwd, ".claude", "commands")];
363
- if (ideDetection.cursor) {
364
- commandDirs.push(path.join(cwd, ".cursor", "commands"));
344
+ else {
345
+ // Create new file
346
+ await mkdir(path.dirname(fullPath), { recursive: true });
347
+ const content = { [target.topLevelKey]: { "bridge-api": buildBridgeApiEntry(cwd) } };
348
+ await writeFile(fullPath, JSON.stringify(content, null, 2) + "\n", "utf-8");
349
+ await ensureGitignored(cwd, target.path);
350
+ configActions.push({ path: target.path, action: "created" });
351
+ anyCreatedOrAdded = true;
365
352
  }
366
- const writtenFiles = new Set();
367
- const skippedFiles = new Set();
368
- const overwrittenFiles = new Set();
369
- for (const dir of commandDirs) {
370
- await mkdir(dir, { recursive: true });
371
- for (const [filename, content] of Object.entries(COMMANDS)) {
372
- const target = path.join(dir, filename);
373
- try {
374
- const existing = await readFile(target, "utf-8");
375
- if (existing === content) {
376
- skippedFiles.add(filename);
377
- continue;
378
- }
379
- overwrittenFiles.add(filename);
353
+ }
354
+ console.log("\nMCP config files:");
355
+ for (const entry of configActions) {
356
+ console.log(` ${entry.path}: ${entry.action}`);
357
+ }
358
+ // ---- Phase 4: Scaffold slash command directories ----
359
+ const commandDirs = [path.join(cwd, ".claude", "commands")];
360
+ if (ideDetection.cursor) {
361
+ commandDirs.push(path.join(cwd, ".cursor", "commands"));
362
+ }
363
+ const writtenFiles = new Set();
364
+ const skippedFiles = new Set();
365
+ const overwrittenFiles = new Set();
366
+ for (const dir of commandDirs) {
367
+ await mkdir(dir, { recursive: true });
368
+ for (const [filename, content] of Object.entries(COMMANDS)) {
369
+ const target = path.join(dir, filename);
370
+ try {
371
+ const existing = await readFile(target, "utf-8");
372
+ if (existing === content) {
373
+ skippedFiles.add(filename);
374
+ continue;
380
375
  }
381
- catch { /* file doesn't exist */ }
382
- await writeFile(target, content, "utf-8");
383
- writtenFiles.add(filename);
376
+ overwrittenFiles.add(filename);
384
377
  }
378
+ catch { /* file doesn't exist */ }
379
+ await writeFile(target, content, "utf-8");
380
+ writtenFiles.add(filename);
385
381
  }
386
- // A file written in any directory takes priority over skipped
387
- for (const f of writtenFiles)
388
- skippedFiles.delete(f);
389
- const total = Object.keys(COMMANDS).length;
390
- const dirNames = commandDirs.map((d) => path.relative(cwd, d)).join(" and ");
391
- console.log(`\nSlash commands: scaffolded ${total} commands into ${commandDirs.length} director${commandDirs.length === 1 ? "y" : "ies"}`);
392
- if (writtenFiles.size > 0)
393
- console.log(` Written: ${writtenFiles.size}`);
394
- if (overwrittenFiles.size > 0)
395
- console.log(` Overwritten (content changed): ${overwrittenFiles.size}`);
396
- if (skippedFiles.size > 0)
397
- console.log(` Skipped (unchanged): ${skippedFiles.size}`);
398
- console.log(` ${dirNames}`);
399
- // ---- Phase 5: Scaffold agent directories ----
400
- const agentWritten = new Set();
401
- const agentSkipped = new Set();
402
- const agentOverwritten = new Set();
403
- // Always scaffold to .claude/agents/
404
- const claudeAgentsDir = path.join(cwd, ".claude", "agents");
405
- await mkdir(claudeAgentsDir, { recursive: true });
382
+ }
383
+ // A file written in any directory takes priority over skipped
384
+ for (const f of writtenFiles)
385
+ skippedFiles.delete(f);
386
+ const total = Object.keys(COMMANDS).length;
387
+ const dirNames = commandDirs.map((d) => path.relative(cwd, d)).join(" and ");
388
+ console.log(`\nSlash commands: scaffolded ${total} commands into ${commandDirs.length} director${commandDirs.length === 1 ? "y" : "ies"}`);
389
+ if (writtenFiles.size > 0)
390
+ console.log(` Written: ${writtenFiles.size}`);
391
+ if (overwrittenFiles.size > 0)
392
+ console.log(` Overwritten (content changed): ${overwrittenFiles.size}`);
393
+ if (skippedFiles.size > 0)
394
+ console.log(` Skipped (unchanged): ${skippedFiles.size}`);
395
+ console.log(` ${dirNames}`);
396
+ // ---- Phase 5: Scaffold agent directories ----
397
+ const agentWritten = new Set();
398
+ const agentSkipped = new Set();
399
+ const agentOverwritten = new Set();
400
+ // Always scaffold to .claude/agents/
401
+ const claudeAgentsDir = path.join(cwd, ".claude", "agents");
402
+ await mkdir(claudeAgentsDir, { recursive: true });
403
+ for (const [key, agent] of Object.entries(AGENTS)) {
404
+ const filename = `${key}.md`;
405
+ const content = reconstructAgentMarkdown(agent.frontmatter, agent.body);
406
+ const target = path.join(claudeAgentsDir, filename);
407
+ try {
408
+ const existing = await readFile(target, "utf-8");
409
+ if (existing === content) {
410
+ agentSkipped.add(filename);
411
+ continue;
412
+ }
413
+ agentOverwritten.add(filename);
414
+ }
415
+ catch { /* file doesn't exist */ }
416
+ await writeFile(target, content, "utf-8");
417
+ agentWritten.add(filename);
418
+ }
419
+ // Scaffold to .github/agents/ for VS Code / Copilot
420
+ if (ideDetection.vscode) {
421
+ const copilotAgentsDir = path.join(cwd, ".github", "agents");
422
+ await mkdir(copilotAgentsDir, { recursive: true });
406
423
  for (const [key, agent] of Object.entries(AGENTS)) {
407
- const filename = `${key}.md`;
408
- const content = reconstructAgentMarkdown(agent.frontmatter, agent.body);
409
- const target = path.join(claudeAgentsDir, filename);
424
+ const filename = `${key}.agent.md`;
425
+ const content = translateAgentToCopilot(agent.frontmatter, agent.body);
426
+ const target = path.join(copilotAgentsDir, filename);
410
427
  try {
411
428
  const existing = await readFile(target, "utf-8");
412
429
  if (existing === content) {
@@ -419,47 +436,27 @@ if (process.argv.includes("--init")) {
419
436
  await writeFile(target, content, "utf-8");
420
437
  agentWritten.add(filename);
421
438
  }
422
- // Scaffold to .github/agents/ for VS Code / Copilot
423
- if (ideDetection.vscode) {
424
- const copilotAgentsDir = path.join(cwd, ".github", "agents");
425
- await mkdir(copilotAgentsDir, { recursive: true });
426
- for (const [key, agent] of Object.entries(AGENTS)) {
427
- const filename = `${key}.agent.md`;
428
- const content = translateAgentToCopilot(agent.frontmatter, agent.body);
429
- const target = path.join(copilotAgentsDir, filename);
430
- try {
431
- const existing = await readFile(target, "utf-8");
432
- if (existing === content) {
433
- agentSkipped.add(filename);
434
- continue;
435
- }
436
- agentOverwritten.add(filename);
437
- }
438
- catch { /* file doesn't exist */ }
439
- await writeFile(target, content, "utf-8");
440
- agentWritten.add(filename);
441
- }
442
- }
443
- // TODO: Add Cursor agent scaffolding when Cursor publishes a formal agent spec
444
- // A file written in any directory takes priority over skipped
445
- for (const f of agentWritten)
446
- agentSkipped.delete(f);
447
- const agentTotal = Object.keys(AGENTS).length;
448
- console.log(`\nAgents: scaffolded ${agentTotal} agent${agentTotal === 1 ? "" : "s"}`);
449
- if (agentWritten.size > 0)
450
- console.log(` Written: ${agentWritten.size}`);
451
- if (agentOverwritten.size > 0)
452
- console.log(` Overwritten (content changed): ${agentOverwritten.size}`);
453
- if (agentSkipped.size > 0)
454
- console.log(` Skipped (unchanged): ${agentSkipped.size}`);
455
- // ---- Phase 6: Scaffold custom pipeline directories ----
456
- const pipelinesDir = path.resolve(cwd, process.env.BAPI_PIPELINES_DIR ?? ".bridge/pipelines");
457
- const instrDir = path.join(path.dirname(pipelinesDir), "instructions");
458
- await mkdir(pipelinesDir, { recursive: true });
459
- await mkdir(instrDir, { recursive: true });
460
- const readmePath = path.join(pipelinesDir, "README.md");
461
- const examplePath = path.join(pipelinesDir, "example-pipeline.json");
462
- const readmeContent = `# Custom Pipelines
439
+ }
440
+ // TODO: Add Cursor agent scaffolding when Cursor publishes a formal agent spec
441
+ // A file written in any directory takes priority over skipped
442
+ for (const f of agentWritten)
443
+ agentSkipped.delete(f);
444
+ const agentTotal = Object.keys(AGENTS).length;
445
+ console.log(`\nAgents: scaffolded ${agentTotal} agent${agentTotal === 1 ? "" : "s"}`);
446
+ if (agentWritten.size > 0)
447
+ console.log(` Written: ${agentWritten.size}`);
448
+ if (agentOverwritten.size > 0)
449
+ console.log(` Overwritten (content changed): ${agentOverwritten.size}`);
450
+ if (agentSkipped.size > 0)
451
+ console.log(` Skipped (unchanged): ${agentSkipped.size}`);
452
+ // ---- Phase 6: Scaffold custom pipeline directories ----
453
+ const pipelinesDir = path.resolve(cwd, process.env.BAPI_PIPELINES_DIR ?? ".bridge/pipelines");
454
+ const instrDir = path.join(path.dirname(pipelinesDir), "instructions");
455
+ await mkdir(pipelinesDir, { recursive: true });
456
+ await mkdir(instrDir, { recursive: true });
457
+ const readmePath = path.join(pipelinesDir, "README.md");
458
+ const examplePath = path.join(pipelinesDir, "example-pipeline.json");
459
+ const readmeContent = `# Custom Pipelines
463
460
 
464
461
  Place custom pipeline JSON files in this directory. They will be loaded at
465
462
  server startup and available alongside the bundled pipelines.
@@ -521,65 +518,80 @@ automatically provided by the server.
521
518
  - \`"halt"\` (default) — stop the pipeline immediately on failure
522
519
  - \`"warn_and_continue"\` — log a warning and proceed to the next step
523
520
  `;
524
- const exampleContent = JSON.stringify({
525
- name: "Example Pipeline",
526
- description: "A sample pipeline demonstrating all step types and features.",
527
- variables: ["ticket_key"],
528
- steps: [
529
- {
530
- type: "mcp_call",
531
- tool: "get_ticket",
532
- params: { ticket_number: "{ticket_key}" },
533
- description: "Fetch the ticket details from Jira",
534
- on_error: "halt",
535
- },
536
- {
537
- type: "agent_task",
538
- instruction: "Read the ticket details above and summarize the key requirements in 3 bullet points.",
539
- description: "Summarize ticket requirements",
540
- on_error: "halt",
541
- },
542
- {
543
- type: "agent_task",
544
- instruction: "Search the codebase for files related to the ticket and list the top 5 most relevant files.",
545
- description: "Find relevant source files",
546
- on_error: "warn_and_continue",
547
- },
548
- {
549
- type: "mcp_call",
550
- tool: "add_comment",
551
- params: {
552
- ticket_number: "{ticket_key}",
553
- comment_text: "Pipeline analysis complete. See agent output for details.",
554
- },
555
- description: "Post a summary comment to the ticket",
556
- on_error: "warn_and_continue",
557
- requires_approval: true,
521
+ const exampleContent = JSON.stringify({
522
+ name: "Example Pipeline",
523
+ description: "A sample pipeline demonstrating all step types and features.",
524
+ variables: ["ticket_key"],
525
+ steps: [
526
+ {
527
+ type: "mcp_call",
528
+ tool: "get_ticket",
529
+ params: { ticket_number: "{ticket_key}" },
530
+ description: "Fetch the ticket details from Jira",
531
+ on_error: "halt",
532
+ },
533
+ {
534
+ type: "agent_task",
535
+ instruction: "Read the ticket details above and summarize the key requirements in 3 bullet points.",
536
+ description: "Summarize ticket requirements",
537
+ on_error: "halt",
538
+ },
539
+ {
540
+ type: "agent_task",
541
+ instruction: "Search the codebase for files related to the ticket and list the top 5 most relevant files.",
542
+ description: "Find relevant source files",
543
+ on_error: "warn_and_continue",
544
+ },
545
+ {
546
+ type: "mcp_call",
547
+ tool: "add_comment",
548
+ params: {
549
+ ticket_number: "{ticket_key}",
550
+ comment_text: "Pipeline analysis complete. See agent output for details.",
558
551
  },
559
- ],
560
- }, null, 2) + "\n";
561
- try {
562
- await stat(readmePath);
563
- console.log(` ${path.relative(cwd, readmePath)} (skipped — already exists)`);
564
- }
565
- catch {
566
- await writeFile(readmePath, readmeContent, "utf-8");
567
- console.log(` ${path.relative(cwd, readmePath)} (written)`);
568
- }
569
- try {
570
- await stat(examplePath);
571
- console.log(` ${path.relative(cwd, examplePath)} (skipped — already exists)`);
572
- }
573
- catch {
574
- await writeFile(examplePath, exampleContent, "utf-8");
575
- console.log(` ${path.relative(cwd, examplePath)} (written)`);
576
- }
577
- console.log(` ${path.relative(cwd, instrDir)}/ (ensured)`);
578
- // ---- Phase 7: Final summary ----
579
- if (anyCreatedOrAdded) {
580
- console.log("\nUpdate BAPI_API_KEY and BAPI_REPO_NAME in your config files — " +
581
- "get these values from the Bridge API setup UI at https://bridgegpt-api.com");
582
- }
552
+ description: "Post a summary comment to the ticket",
553
+ on_error: "warn_and_continue",
554
+ requires_approval: true,
555
+ },
556
+ ],
557
+ }, null, 2) + "\n";
558
+ try {
559
+ await stat(readmePath);
560
+ console.log(` ${path.relative(cwd, readmePath)} (skipped — already exists)`);
561
+ }
562
+ catch {
563
+ await writeFile(readmePath, readmeContent, "utf-8");
564
+ console.log(` ${path.relative(cwd, readmePath)} (written)`);
565
+ }
566
+ try {
567
+ await stat(examplePath);
568
+ console.log(` ${path.relative(cwd, examplePath)} (skipped — already exists)`);
569
+ }
570
+ catch {
571
+ await writeFile(examplePath, exampleContent, "utf-8");
572
+ console.log(` ${path.relative(cwd, examplePath)} (written)`);
573
+ }
574
+ console.log(` ${path.relative(cwd, instrDir)}/ (ensured)`);
575
+ // ---- Phase 7: Final summary ----
576
+ if (anyCreatedOrAdded) {
577
+ console.log("\nUpdate BAPI_API_KEY and BAPI_REPO_NAME in your config files — " +
578
+ "get these values from the Bridge API setup UI at https://bridgegpt-api.com");
579
+ }
580
+ }
581
+ // ---------------------------------------------------------------------------
582
+ // CLI: --init
583
+ // ---------------------------------------------------------------------------
584
+ if (process.argv.includes("--init")) {
585
+ try {
586
+ await stat(path.join(process.cwd(), "package.json"));
587
+ }
588
+ catch {
589
+ console.error("Error: No package.json found in current directory.\n" +
590
+ "--init must be run from your project root (the directory containing package.json).");
591
+ process.exit(1);
592
+ }
593
+ try {
594
+ await runInit(process.cwd());
583
595
  process.exit(0);
584
596
  }
585
597
  catch (err) {
@@ -589,6 +601,43 @@ automatically provided by the server.
589
601
  }
590
602
  }
591
603
  // ---------------------------------------------------------------------------
604
+ // CLI: --upgrade
605
+ // ---------------------------------------------------------------------------
606
+ if (process.argv.includes("--upgrade")) {
607
+ try {
608
+ await stat(path.join(process.cwd(), "package.json"));
609
+ }
610
+ catch {
611
+ console.error("Error: No package.json found in current directory.\n" +
612
+ "--upgrade must be run from your project root (the directory containing package.json).");
613
+ process.exit(1);
614
+ }
615
+ try {
616
+ const cwd = process.cwd();
617
+ const oldVersion = VERSION;
618
+ console.log("Upgrading @bridge_gpt/mcp-server to latest...\n");
619
+ execSync("npm i @bridge_gpt/mcp-server@latest", { stdio: "inherit" });
620
+ // Read the newly installed version from node_modules
621
+ const require = createRequire(cwd + "/");
622
+ const newPkgPath = require.resolve("@bridge_gpt/mcp-server/package.json");
623
+ const newPkg = JSON.parse(await readFile(newPkgPath, "utf-8"));
624
+ const newVersion = newPkg.version;
625
+ console.log(`\n@bridge_gpt/mcp-server: ${oldVersion} -> ${newVersion}`);
626
+ if (oldVersion === newVersion) {
627
+ console.log("Already up-to-date.");
628
+ }
629
+ console.log("\nRefreshing scaffolded artifacts...\n");
630
+ await runInit(cwd);
631
+ console.log("\nUpgrade complete.");
632
+ process.exit(0);
633
+ }
634
+ catch (err) {
635
+ const msg = err instanceof Error ? err.message : String(err);
636
+ console.error(`Bridge API --upgrade failed: ${msg}`);
637
+ process.exit(1);
638
+ }
639
+ }
640
+ // ---------------------------------------------------------------------------
592
641
  // Server
593
642
  // ---------------------------------------------------------------------------
594
643
  const server = new McpServer({
@@ -612,8 +661,8 @@ server.registerTool("ping", {
612
661
  return { content: [{ type: "text", text }] };
613
662
  });
614
663
  server.registerTool("get_project_standards", {
615
- description: "Retrieve project-specific coding standards, architecture guidelines, testing standards, and code review standards for the configured repository. " +
616
- "Returns structured markdown with sections for architecture instructions, code review correctness standards, testing stack information, and build analysis. " +
664
+ description: "Retrieve project-specific coding standards, architecture guidelines, testing standards, code review standards, and project context (platform, version, project description) for the configured repository. " +
665
+ "Returns structured markdown with sections for project context, architecture instructions, code review correctness standards, testing stack information, and build analysis. " +
617
666
  "Only sections with configured values are included. Returns 404 if no standards are configured. " +
618
667
  "Consult these standards before writing or reviewing code to ensure compliance with project conventions.",
619
668
  inputSchema: {},
@@ -1022,6 +1071,119 @@ server.registerTool("upload_attachment", {
1022
1071
  const text = await handleResponse(resp);
1023
1072
  return { content: [{ type: "text", text: text + resolved.note }] };
1024
1073
  });
1074
+ server.registerTool("list_attachments", {
1075
+ description: "List attachments on a Jira ticket. " +
1076
+ "Returns metadata (ID, filename, MIME type, size, created date) for each attachment. " +
1077
+ "By default, AI-generated attachments are excluded. " +
1078
+ "Use this to discover available attachments before downloading.",
1079
+ inputSchema: {
1080
+ ticket_number: z
1081
+ .string()
1082
+ .describe("Jira ticket key in PROJECT-NUMBER format (e.g. PROJ-123)"),
1083
+ include_ai_generated: z
1084
+ .boolean()
1085
+ .optional()
1086
+ .describe("Include AI-generated attachments in the list (default: false)"),
1087
+ },
1088
+ }, async ({ ticket_number, include_ai_generated }) => {
1089
+ const params = { repo_name: REPO_NAME };
1090
+ if (include_ai_generated) {
1091
+ params.include_ai_generated = "true";
1092
+ }
1093
+ const url = buildGetUrl(`/ticket/${encodeURIComponent(ticket_number)}/attachments`, params);
1094
+ const resp = await fetch(url, { headers: GET_HEADERS });
1095
+ const text = await handleResponse(resp);
1096
+ return { content: [{ type: "text", text }] };
1097
+ });
1098
+ const MAX_INLINE_TEXT_LENGTH = 50_000;
1099
+ server.registerTool("download_attachment", {
1100
+ description: "Download an attachment from a Jira ticket and save it to disk. " +
1101
+ "Specify either attachment_id or filename (not both). " +
1102
+ "For text files, the content is returned inline (truncated at ~50KB) and also saved to disk. " +
1103
+ "For binary files, the file is saved to disk and the path is returned. " +
1104
+ "Default save location: {BAPI_DOCS_DIR}/attachments/{ticket_number}/{filename}.",
1105
+ inputSchema: {
1106
+ ticket_number: z
1107
+ .string()
1108
+ .describe("Jira ticket key in PROJECT-NUMBER format (e.g. PROJ-123)"),
1109
+ attachment_id: z
1110
+ .string()
1111
+ .optional()
1112
+ .describe("Jira attachment ID. Mutually exclusive with filename."),
1113
+ filename: z
1114
+ .string()
1115
+ .optional()
1116
+ .describe("Attachment filename. If multiple exist, returns the most recent. Mutually exclusive with attachment_id."),
1117
+ file_path: z
1118
+ .string()
1119
+ .optional()
1120
+ .describe("Override the default save location. If omitted, saves to {BAPI_DOCS_DIR}/attachments/{ticket_number}/{filename}."),
1121
+ },
1122
+ }, async ({ ticket_number, attachment_id, filename, file_path }) => {
1123
+ if (!attachment_id && !filename) {
1124
+ return {
1125
+ content: [{
1126
+ type: "text",
1127
+ text: JSON.stringify({
1128
+ error: "VALIDATION_ERROR",
1129
+ message: "Provide either attachment_id or filename (at least one is required).",
1130
+ }),
1131
+ }],
1132
+ };
1133
+ }
1134
+ const params = { repo_name: REPO_NAME };
1135
+ if (attachment_id)
1136
+ params.attachment_id = attachment_id;
1137
+ if (filename)
1138
+ params.filename = filename;
1139
+ const url = buildGetUrl(`/ticket/${encodeURIComponent(ticket_number)}/attachments/download`, params);
1140
+ const resp = await fetch(url, { headers: GET_HEADERS });
1141
+ if (!resp.ok) {
1142
+ const text = await handleResponse(resp);
1143
+ return { content: [{ type: "text", text }] };
1144
+ }
1145
+ const body = await resp.json();
1146
+ const serverFilename = body.filename;
1147
+ const content = body.content;
1148
+ const isText = body.is_text;
1149
+ const mimeType = body.mime_type;
1150
+ const size = body.size;
1151
+ const safeFileName = path.basename(serverFilename);
1152
+ const safeTicket = path.basename(ticket_number);
1153
+ const savePath = file_path
1154
+ ? file_path
1155
+ : path.join(BAPI_DOCS_DIR, "attachments", safeTicket, safeFileName);
1156
+ const resolvedSave = path.resolve(savePath);
1157
+ const resolvedRoot = path.resolve(PROJECT_ROOT);
1158
+ if (!resolvedSave.startsWith(resolvedRoot + path.sep) && resolvedSave !== resolvedRoot) {
1159
+ return {
1160
+ content: [{
1161
+ type: "text",
1162
+ text: JSON.stringify({
1163
+ error: "VALIDATION_ERROR",
1164
+ message: `Save path "${savePath}" is outside the project root. Refusing to write.`,
1165
+ }),
1166
+ }],
1167
+ };
1168
+ }
1169
+ await mkdir(path.dirname(resolvedSave), { recursive: true });
1170
+ if (isText) {
1171
+ await writeFile(resolvedSave, content, "utf-8");
1172
+ }
1173
+ else {
1174
+ await writeFile(resolvedSave, Buffer.from(content, "base64"));
1175
+ }
1176
+ let resultText = `File saved to: ${resolvedSave}\nFilename: ${safeFileName}\nMIME type: ${mimeType}\nSize: ${size} bytes`;
1177
+ if (isText) {
1178
+ if (content.length > MAX_INLINE_TEXT_LENGTH) {
1179
+ resultText += `\n\n${content.slice(0, MAX_INLINE_TEXT_LENGTH)}\n\n[Content truncated. Full content saved to ${resolvedSave}]`;
1180
+ }
1181
+ else {
1182
+ resultText += `\n\n${content}`;
1183
+ }
1184
+ }
1185
+ return { content: [{ type: "text", text: resultText }] };
1186
+ });
1025
1187
  server.registerTool("request_plan_generation", {
1026
1188
  description: "Request AI-generated implementation plan for a Jira ticket. " +
1027
1189
  "This triggers an asynchronous background job — results are NOT immediate. " +
@@ -1516,11 +1678,13 @@ server.registerTool("update_jira_status", {
1516
1678
  description: "Transition a Jira ticket to a specified target status by executing a workflow transition. " +
1517
1679
  "Provide either target_status (matched case-insensitively against available transitions) or transition_id (used directly). " +
1518
1680
  "If transition_id is provided, it takes precedence over target_status. " +
1681
+ 'Pass target_status as "auto" to trigger server-side status resolution via LLM — the server determines the correct ' +
1682
+ "post-PR status automatically. If auto-resolve finds no match, returns status: skipped (not an error). " +
1519
1683
  "Returns the from/to status on success, or an error listing available transitions if no match is found. " +
1520
1684
  "The repo_name is automatically injected from the configured environment.",
1521
1685
  inputSchema: {
1522
1686
  ticket_number: z.string().describe("Jira ticket key in PROJECT-NUMBER format (e.g. PROJ-123)"),
1523
- target_status: z.string().optional().describe("Target status name to transition to (case-insensitive match)"),
1687
+ target_status: z.string().optional().describe('Target status name to transition to (case-insensitive match). Pass "auto" to resolve the target status server-side via LLM agent.'),
1524
1688
  transition_id: z.string().optional().describe("Specific transition ID to execute (takes precedence over target_status)"),
1525
1689
  },
1526
1690
  }, async ({ ticket_number, target_status, transition_id }) => {
@@ -1570,6 +1734,7 @@ const VALID_CONFIG_FIELDS = [
1570
1734
  "unit_testing_instructions", "e2e_testing_instructions",
1571
1735
  "frontend_correctness_standards", "backend_correctness_standards",
1572
1736
  "template_correctness_standards", "style_correctness_standards",
1737
+ "design_principles",
1573
1738
  "post_pr_target_status", "ci_check_config",
1574
1739
  ].join(", ");
1575
1740
  server.registerTool("list_config_fields", {
@@ -2141,3 +2306,15 @@ userPipelineKeys = customResult.userPipelineKeys;
2141
2306
  const transport = new StdioServerTransport();
2142
2307
  await server.connect(transport);
2143
2308
  console.error("Bridge API MCP server running on stdio");
2309
+ // Fire-and-forget update check — delay to let MCP client attach listeners
2310
+ (async () => {
2311
+ await new Promise((r) => setTimeout(r, 2000));
2312
+ const result = await checkForUpdate();
2313
+ if (result?.updateAvailable) {
2314
+ server.server.sendLoggingMessage({
2315
+ level: "notice",
2316
+ logger: "bridge-api",
2317
+ data: `Update available: @bridge_gpt/mcp-server ${result.currentVersion} -> ${result.latestVersion}. Run: npx -y @bridge_gpt/mcp-server --upgrade`,
2318
+ });
2319
+ }
2320
+ })().catch(() => { });
@@ -175,7 +175,7 @@ export function resolveRecipe(pipeline, instructions, variables, skipSteps) {
175
175
  agent_instructions: "IMPORTANT: Execute every step below in exact sequential order. " +
176
176
  "For mcp_call steps, call the specified tool with the provided params. " +
177
177
  "For agent_task steps, follow the instruction text using any tools it specifies. " +
178
- "If requires_approval is true, pause and ask the user for confirmation before executing. " +
178
+ "If requires_approval is true, pause before executing. For agent_task steps, the instruction file's own approval format is authoritative — follow it verbatim and do not substitute your own short confirmation prompt. For mcp_call steps with no instruction file, present the resolved params as bullet points and ask for approval. " +
179
179
  'For on_error "halt", stop the pipeline immediately on failure. ' +
180
180
  'For on_error "warn_and_continue", log a warning and proceed. ' +
181
181
  "Do not skip steps, reorder them, or substitute your own tool calls.",
@@ -90,9 +90,9 @@ export const PIPELINES = {
90
90
  "tool": "update_jira_status",
91
91
  "params": {
92
92
  "ticket_number": "{ticket_key}",
93
- "target_status": "In Review"
93
+ "target_status": "auto"
94
94
  },
95
- "description": "Transition ticket to In Review",
95
+ "description": "Resolve and transition ticket to post-PR status",
96
96
  "on_error": "warn_and_continue"
97
97
  },
98
98
  {
@@ -335,6 +335,30 @@ export const PIPELINES = {
335
335
  },
336
336
  "description": "Upload style_correctness_standards to config",
337
337
  "requires_approval": true
338
+ },
339
+ {
340
+ "type": "mcp_call",
341
+ "tool": "get_config_field",
342
+ "params": {
343
+ "field_name": "design_principles"
344
+ },
345
+ "description": "Fetch current design_principles value",
346
+ "on_error": "warn_and_continue"
347
+ },
348
+ {
349
+ "type": "agent_task",
350
+ "instruction_file": "learn-design-principles.md",
351
+ "description": "Learn and document design principles"
352
+ },
353
+ {
354
+ "type": "mcp_call",
355
+ "tool": "update_config_field",
356
+ "params": {
357
+ "field_name": "design_principles",
358
+ "file_path": "{docs_dir}/standards/design_principles.md"
359
+ },
360
+ "description": "Upload design_principles to config",
361
+ "requires_approval": true
338
362
  }
339
363
  ]
340
364
  },
@@ -502,16 +526,17 @@ export const PIPELINES = {
502
526
  };
503
527
  export const INSTRUCTIONS = {
504
528
  "assess-epic-research-needs.md": "Analyze the epic description and build a structured research plan.\n\n## Epic Description\n\n{epic_description}\n\n## Instructions\n\n1. Create the directory structure for this epic's artifacts:\n ```\n mkdir -p {docs_dir}/epic-plans/{epic_slug}\n ```\n\n2. Analyze the epic description above. Determine what external knowledge is required to plan this epic effectively. Consider:\n - Unfamiliar technologies, libraries, or frameworks mentioned\n - API documentation or integration specs that need to be consulted\n - Best practices or architectural patterns that require research\n - Domain-specific knowledge gaps\n\n3. Decide on a **Research Mode**:\n - **deep**: Use when the epic involves large, multi-faceted unknowns requiring synthesis from multiple sources (e.g., \"best practices for implementing WebSocket connection pooling in Python asyncio\").\n - **web**: Use for quick factual lookups — library API signatures, configuration syntax, small \"how to\" questions.\n - **none**: Use when the codebase exploration alone will provide sufficient context and no external knowledge is needed.\n\n4. Write a structured research plan to `{docs_dir}/epic-plans/{epic_slug}/research-plan.md` with these sections:\n\n```markdown\n# Research Plan\n\n## Research Mode\n{deep | web | none}\n\n## Deep Research Query\n{If mode is \"deep\": a single, well-crafted query for the deep research tool. Otherwise: \"N/A\"}\n\n## Web Search Topics\n{If mode is \"web\" or as fallback topics for \"deep\": a numbered list of specific search topics. Otherwise: \"N/A\"}\n\n## Rationale\n{Brief explanation of why this research mode was chosen and what knowledge gaps it addresses.}\n```\n",
505
- "capture-review-decisions.md": "Capture user decisions on review findings for {ticket_key} using the HTML decision page, then interpretively rewrite the clarifying questions and critique docs and upload both to Jira.\n\n## Step 1: Read source documents\n\nRead both files:\n- Evaluation: `{docs_dir}/review/{ticket_key}-review-evaluation.md`\n- Resolution guide: `{docs_dir}/review/{ticket_key}-resolution-guide.md`\n\nIf either file does not exist or is empty, stop and report: \"Review evaluation or resolution guide not found. Run the earlier pipeline steps first.\"\n\n## Step 2: Map evaluation items to decision page input\n\nTransform the evaluation into `generate_decision_page` JSON input using these mapping rules:\n\n| Evaluation Section | JSON Field | Mapping Rule |\n|---|---|---|\n| Open Questions | `actionable_items` | E-item title → `question`, assessment + codebase evidence → `context`, `**Source**` from the evaluation → `source`, decision tree branch texts from the resolution guide → `options` (string array, labels only), `**Recommendation Index**` from the resolution guide → `recommendation_index` |\n| Needs Scrutiny | `actionable_items` | E-item title → `question`, assessment + evidence → `context`, `**Source**` from the evaluation → `source`, decision tree branch texts from the resolution guide → `options` (string array, labels only), `**Recommendation Index**` from the resolution guide → `recommendation_index` |\n| Confirmed Improvements | `clear_improvements` | E-item title → `title`, confidence tag → `confidence`, recommended action → `action`, `**Source**` from the evaluation → `source` |\n\nFor each actionable item, the `options` array is a list of plain label strings extracted from the resolution guide's decision tree branches. The tool auto-generates value keys (`opt-0`, `opt-1`, etc.) and auto-appends a \"None of these\" option. Do not generate value keys yourself.\n\n## Step 3: Call the MCP tool\n\nCall `generate_decision_page` with:\n- `ticket_key`: `{ticket_key}`\n- `actionable_items`: combined mapped array from Step 2 (each item has `id`, `question`, `context`, `source`, `recommendation_index`, `options`)\n- `clear_improvements`: mapped array from Step 2 (each item has `id`, `title`, `action`, `confidence`, `source`)\n\n## Step 4: Check tool response\n\nThe tool returns a JSON response with a `status` field:\n- If `status` is `\"no_decisions_needed\"`: skip Steps 5, 6, 7, and 8 entirely. Output a success message: \"All suggestions were confirmed as improvements. No decisions needed — skipping doc rewrite and upload.\"\n- If `status` is `\"decision_page_generated\"`: continue to Step 5. The response includes `file_path`.\n\n## Step 5: Direct user to the decision page\n\nTell the user to open the generated HTML file in their browser. Provide the `file_path` from the tool response.\n\n## Step 6: Wait for user input\n\nStop and wait for the user to paste the JSON output from the decision page. Do NOT assume responses. Do NOT proceed until the user provides the JSON.\n\n## Step 7: Interpretively rewrite source documents\n\nThe pasted JSON contains a `decisions` object keyed by item ID. Each decision includes `source`, `choice`, `chosen_label`, and `comment`. Use these fields to locate and rewrite the corresponding sections in:\n- `{docs_dir}/clarifying-questions/{ticket_key}-clarifying-questions.md`\n- `{docs_dir}/ticket-critiques/{ticket_key}-ticket-quality-critique.md`\n\nFor each decision, use the `source` field (e.g., \"Clarifying Q3 (initial round)\") to find the original section in the appropriate document. Then apply the decision:\n\n### Actionable item decisions\n\n- **Selected option** (`choice` is `opt-N`): Add `**Review Decision**: Accepted. <chosen_label>.` to the corresponding section. Integrate the selected direction into the section text so it reads as a final recommendation or resolved answer.\n- **None of these** (`choice` is `none`): Add `**Review Decision**: Rejected — none of the proposed options accepted.` Include the user's `comment` explaining why. Rewrite the section to reflect this decision.\n\nFor actionable items sourced from clarifying questions, rewrite the question's best-guess answer so it reads as the final resolved direction chosen by the reviewer. Do not leave the item framed as an unresolved accept/reject/modify prompt.\n\n### General comment handling\n\nTreat `general_comment` as overarching guidance that informs the tone and direction of both document rewrites. If it contains specific actionable feedback, weave it into the relevant sections. If it is broad or general, use it as context for how the rewrites should read. Do not create a separate \"General Comment\" or \"Reviewer Notes\" section — the goal is \"final draft\" form.\n\n### Rewrite principles\n\nThe goal is a **final draft** — the documents should read as if they were written with the decisions already made. Do not mechanically append decisions. Instead, lightly rewrite affected sections so they reflect the decisions naturally. Preserve all non-affected sections unchanged.\n\n## Step 8: Upload to Jira\n\nUpload both updated documents to Jira using `upload_attachment`:\n\n1. Upload clarifying questions:\n - `ticket_number`: `{ticket_key}`\n - `file_path`: `{docs_dir}/clarifying-questions/{ticket_key}-clarifying-questions.md`\n - `link_type`: `clarifying-questions.md`\n\n2. Upload ticket quality critique:\n - `ticket_number`: `{ticket_key}`\n - `file_path`: `{docs_dir}/ticket-critiques/{ticket_key}-ticket-quality-critique.md`\n - `link_type`: `ticket-quality-critique.md`\n\n## Step 9: Complete\n\nConfirm: \"Review decisions captured and uploaded to {ticket_key}.\"\n\nThis is a single-round process. Do not iterate or ask for additional feedback rounds.\n",
506
- "commit-and-push.md": "Stage all changed files and create a commit for ticket {ticket_key}.\n\n1. Run `git add` on all files modified or created during implementation. Do not stage unrelated files.\n2. Create a commit with the message: `{ticket_key}: Implementation`\n3. Push the current branch to the remote.\n",
507
- "decompose-epic.md": "Decompose the epic into manageable sub-tasks and get user approval.\n\n## Epic Description\n\n{epic_description}\n\n## Instructions\n\n1. Read the following artifacts to establish full context. If a file does not exist or is empty, proceed without it:\n - `{docs_dir}/epic-plans/{epic_slug}/research-findings.md`\n - `{docs_dir}/epic-plans/{epic_slug}/codebase-exploration.md`\n\n2. Reason about the epic and produce a decomposition. Consider:\n - Logical groupings of work that can be implemented and tested independently\n - Dependencies between sub-tasks (what must be built first)\n - A reasonable scope for each sub-task (each should be achievable in a single implementation session)\n\n3. Write the decomposition to `{docs_dir}/epic-plans/{epic_slug}/decomposition.md` with this format:\n\n```markdown\n# Epic Decomposition\n\n## Sub-tasks\n\n### 1. {Sub-task title}\n- **Scope**: {What this sub-task covers}\n- **Key files/areas**: {Files and code areas involved}\n- **Dependencies**: {Other sub-task numbers this depends on, or \"None\"}\n\n### 2. {Sub-task title}\n...\n```\n\n4. **Soft limit check**: If the decomposition results in more than 8 sub-tasks, you must verbally warn the user: \"This decomposition has N sub-tasks, which exceeds the recommended limit of 8. Consider splitting this feature into multiple epics.\" Then proceed with the approval flow.\n\n5. Present the decomposition to the user and ask for their feedback. Explain the reasoning behind the breakdown and the dependency ordering.\n\n6. You MUST stop and wait for the user to respond. Do NOT assume approval. Do NOT proceed to the next step.\n\n7. If the user provides feedback or rejects the decomposition:\n - Incorporate their feedback\n - Rewrite `{docs_dir}/epic-plans/{epic_slug}/decomposition.md` with the revised version\n - Present the revised decomposition and ask for approval again\n - Repeat until the user explicitly approves\n\n8. Only after explicit user approval, confirm: \"Decomposition approved. Proceeding to sub-task exploration.\"\n",
508
- "evaluate-ticket-review.md": "Evaluate the clarifying questions and ticket critiques generated for {ticket_key}, assessing their accuracy and value against the actual codebase.\n\n1. Fetch the current ticket description using the `get_ticket` tool with ticket_number `{ticket_key}`.\n2. Gather all clarifying questions and critiques from the preceding pipeline steps. Both initial and second-opinion rounds may be present — use the MCP call results from the conversation context, since the files on disk at `{docs_dir}/clarifying-questions/{ticket_key}-clarifying-questions.md` and `{docs_dir}/ticket-critiques/{ticket_key}-ticket-quality-critique.md` only reflect the most recent round.\n3. For each clarifying question and its best-guess answer, verify groundedness against the codebase using file search and code grep. Apply the following depth rules based on round agreement:\n - **Both rounds agree** (same question raised, similar answer): Briefly confirm or refute with a single codebase citation. Keep the assessment to 1 sentence.\n - **Rounds disagree** (one round flags it and the other doesn't, or they reach opposite conclusions): Perform deeper analysis — cite 2+ codebase locations, explain the discrepancy, and state which round's position the evidence supports.\n - **Single round only** (no second opinion for this item): Treat as a disagreement — give full analytical depth.\n Then categorize:\n - If the question/answer accurately reflects the codebase, confirm it as grounded with evidence.\n - If the question/answer misunderstands the codebase (wrong assumptions about architecture, incorrect file references, etc.), call it out with evidence of what the code actually does.\n - If the question raises a genuine gap that cannot be resolved from the codebase alone, note it as a legitimate open question.\n4. For each critique point (Requested Changes and Points to Consider), apply the same depth rules from step 3 based on round agreement, then assess:\n - Does it identify a genuine gap or design issue in the ticket? Confirm with codebase evidence.\n - Is it based on an inaccurate understanding of the existing code? Flag with corrections.\n - Is it a stylistic preference rather than a substantive improvement? Note as low-priority.\n5. Produce a structured evaluation with three sections, following the formatting rules below. Number each item sequentially across all sections (E-1, E-2, E-3, ...).\n\n## Formatting Rules\n\nEach item must follow this template:\n\n```\n### E-<sequential number>: <concise title>\n\n**Source**: <which clarifying question or critique point this originated from, e.g. \"Clarifying Q3 (initial round)\" or \"Critique: Requested Change 2 (second opinion)\">\n\n**Round Agreement**: <\"both rounds agree\" | \"rounds disagree\" | \"single round only\"> — <if disagreeing, 1 sentence on what the disagreement is>\n\n**Assessment**: <1-2 sentence summary of what was found>\n\n**Codebase Evidence**:\n- `path/to/file.ts:42` — <what this line/block demonstrates>\n- `path/to/other.ts:110-125` — <what this range demonstrates>\n\n<If no direct codebase evidence exists, state: \"No direct codebase evidence found.\">\n```\n\n**Depth calibration**: When Round Agreement is \"both rounds agree\", keep Assessment to 1 sentence and Codebase Evidence to 1 citation. When Round Agreement is \"rounds disagree\" or \"single round only\", Assessment should be 2 sentences and Codebase Evidence should cite 2+ files explaining the discrepancy.\n\n## Output Sections\n\n- **Confirmed Improvements**: Suggestions that are grounded and would genuinely improve the ticket by closing significant gaps or correcting design issues.\n- **Needs Scrutiny**: Suggestions based on inaccurate codebase assumptions, with evidence of the actual code behavior.\n- **Open Questions**: Legitimate ambiguities that require human input to resolve.\n\n6. Save the evaluation to `{docs_dir}/review/{ticket_key}-review-evaluation.md`. Output only the structured evaluation — no meta-commentary.\n",
529
+ "capture-review-decisions.md": "Capture user decisions on review findings for {ticket_key} using the HTML decision page, then interpretively rewrite the clarifying questions and critique docs and upload both to Jira.\n\n## Step 1: Read source documents\n\nRead both files:\n- Evaluation: `{docs_dir}/review/{ticket_key}-review-evaluation.md`\n- Resolution guide: `{docs_dir}/review/{ticket_key}-resolution-guide.md`\n\nIf either file does not exist or is empty, stop and report: \"Review evaluation or resolution guide not found. Run the earlier pipeline steps first.\"\n\n## Step 2: Map evaluation items to decision page input\n\nTransform the evaluation into `generate_decision_page` JSON input using these mapping rules:\n\n| Evaluation Section | JSON Field | Mapping Rule |\n|---|---|---|\n| Open Questions | `actionable_items` | E-item title → `question`, full **Assessment** text → `context`, `**Source**` from the evaluation → `source`, decision tree branch texts from the resolution guide → `options` (string array, labels only), `**Recommendation Index**` from the resolution guide → `recommendation_index` |\n| Needs Scrutiny | `actionable_items` | E-item title → `question`, full **Assessment** text → `context`, `**Source**` from the evaluation → `source`, decision tree branch texts from the resolution guide → `options` (string array, labels only), `**Recommendation Index**` from the resolution guide → `recommendation_index` |\n| Confirmed Improvements | `clear_improvements` | E-item title → `title`, confidence tag → `confidence`, recommended action → `action`, `**Source**` from the evaluation → `source` |\n\n**Important**: The `context` field guides the user's decision, therefore, it is important to present all relevant information to the the user in clear and straightforward terms. Your goal is to make it easy for the user to make a decision. Do not assume the user is familiar with the underlying documents. \n\nFor each actionable item, the `options` array is a list of plain label strings extracted from the resolution guide's decision tree branches. The tool auto-generates value keys (`opt-0`, `opt-1`, etc.) and auto-appends a \"None of these\" option. Do not generate value keys yourself.\n\n## Step 3: Call the MCP tool\n\nCall `generate_decision_page` with:\n- `ticket_key`: `{ticket_key}`\n- `actionable_items`: combined mapped array from Step 2 (each item has `id`, `question`, `context`, `source`, `recommendation_index`, `options`)\n- `clear_improvements`: mapped array from Step 2 (each item has `id`, `title`, `action`, `confidence`, `source`)\n\n## Step 4: Check tool response\n\nThe tool returns a JSON response with a `status` field:\n- If `status` is `\"no_decisions_needed\"`: skip Steps 5, 6, 7, and 8 entirely. Output a success message: \"All suggestions were confirmed as improvements. No decisions needed — skipping doc rewrite and upload.\"\n- If `status` is `\"decision_page_generated\"`: continue to Step 5. The response includes `file_path`.\n\n## Step 5: Direct user to the decision page\n\nTell the user to open the generated HTML file in their browser. Provide the `file_path` from the tool response.\n\n## Step 6: Wait for user input\n\nStop and wait for the user to paste the JSON output from the decision page. Do NOT assume responses. Do NOT proceed until the user provides the JSON.\n\n## Step 7: Interpretively rewrite source documents\n\nThe pasted JSON contains a `decisions` object keyed by item ID. Each decision includes `source`, `choice`, `chosen_label`, and `comment`. Use these fields to locate and rewrite the corresponding sections in:\n- `{docs_dir}/clarifying-questions/{ticket_key}-clarifying-questions.md`\n- `{docs_dir}/ticket-critiques/{ticket_key}-ticket-quality-critique.md`\n\nFor each decision, use the `source` field (e.g., \"Clarifying Q3 (initial round)\") to find the original section in the appropriate document. Then apply the decision:\n\n### Actionable item decisions\n\n- **Selected option** (`choice` is `opt-N`): Add `**Review Decision**: Accepted. <chosen_label>.` to the corresponding section. Integrate the selected direction into the section text so it reads as a final recommendation or resolved answer.\n- **None of these** (`choice` is `none`): Add `**Review Decision**: Rejected — none of the proposed options accepted.` Include the user's `comment` explaining why. Rewrite the section to reflect this decision.\n\nFor actionable items sourced from clarifying questions, rewrite the question's best-guess answer so it reads as the final resolved direction chosen by the reviewer. Do not leave the item framed as an unresolved accept/reject/modify prompt.\n\n### General comment handling\n\nTreat `general_comment` as overarching guidance that informs the tone and direction of both document rewrites. If it contains specific actionable feedback, weave it into the relevant sections. If it is broad or general, use it as context for how the rewrites should read. Do not create a separate \"General Comment\" or \"Reviewer Notes\" section — the goal is \"final draft\" form.\n\n### Rewrite principles\n\nThe goal is a **final draft** — the documents should read as if they were written with the decisions already made. Do not mechanically append decisions. Instead, lightly rewrite affected sections so they reflect the decisions naturally. Preserve all non-affected sections unchanged.\n\n## Step 8: Upload to Jira\n\nUpload both updated documents to Jira using `upload_attachment`:\n\n1. Upload clarifying questions:\n - `ticket_number`: `{ticket_key}`\n - `file_path`: `{docs_dir}/clarifying-questions/{ticket_key}-clarifying-questions.md`\n - `link_type`: `clarifying-questions.md`\n\n2. Upload ticket quality critique:\n - `ticket_number`: `{ticket_key}`\n - `file_path`: `{docs_dir}/ticket-critiques/{ticket_key}-ticket-quality-critique.md`\n - `link_type`: `ticket-quality-critique.md`\n\n## Step 9: Complete\n\nConfirm: \"Review decisions captured and uploaded to {ticket_key}.\"\n\nThis is a single-round process. Do not iterate or ask for additional feedback rounds.\n",
530
+ "commit-and-push.md": "Stage, commit, and push implementation changes for ticket {ticket_key}.\n\nBefore executing, assess the git state and present a clear plan for user approval.\n\n## Step 1 — Assess Git State\n\nRun these commands and note the results:\n- `git branch --show-current` record the current branch name\n- `git status --porcelain` — identify all modified, added, and untracked files\n\n## Step 2 — Determine Branch\n\nDecide the branching strategy and be prepared to state it explicitly. Cover:\n\n- Whether you will commit on the current branch, or create a new branch.\n- If creating a new branch: the exact new branch name, and which branch it will be created from (current branch vs. `main`).\n- If branching from `main`: whether `main` needs to be pulled/updated first, and the command you will run.\n- Whether the target branch already exists remotely (and if so, whether you will push to the existing remote branch).\n\nDefault rules:\n\n- If the current branch already contains `{ticket_key}` (case-insensitive), plan to commit on the current branch.\n- Otherwise, plan to create a new branch named `feature/{ticket_key}` from the current branch.\n\n## Step 3 — Prepare Commit Details\n\n- Separate implementation files from unrelated changes. Only stage files related to the ticket.\n- Compose a commit message: `{ticket_key}: <brief description of what was implemented>`\n\n## Step 4 — Present Plan for Approval\n\nPresent your plan in this format before proceeding:\n\n```\nCommit Plan for {ticket_key}\n─────────────────────────────\nCurrent branch: <current branch name>\nBranching: - <\"Commit on current branch\" | \"Create new branch `<name>` from `<source branch>`\">\n - <if branching from main: \"Pull latest main first via `git checkout main && git pull`\" | omit if N/A>\n - <\"Remote branch already exists — will push to existing\" | \"New remote branch — will push with -u\" | omit if N/A>\nFiles to stage: <count> files\n - path/to/file1.py\n - path/to/file2.py\nExcluded: <any unrelated changed files, or \"None\">\nCommit message: {ticket_key}: <description>\nPush to: origin/<target branch>\n```\n\nWait for the user to approve, request changes, or reject. The user may adjust the branch name, exclude files, change the commit message, or give other instructions.\n\nDo not proceed until the user explicitly approves.\n\n## Step 5 — Execute\n\n1. If creating a new branch, run `git checkout -b <branch name>`.\n2. Stage approved files with `git add <file1> <file2> ...` — do not use `git add -A` or `git add .`.\n3. Commit with the approved message.\n4. Push with `git push -u origin <branch>`.\n",
531
+ "decompose-epic.md": "Decompose the epic into manageable sub-tasks and get user approval.\n\n## Epic Description\n\n{epic_description}\n\n## Instructions\n\n1. Read the following artifacts to establish full context. If a file does not exist or is empty, proceed without it:\n - `{docs_dir}/epic-plans/{epic_slug}/research-findings.md`\n - `{docs_dir}/epic-plans/{epic_slug}/codebase-exploration.md`\n\n2. Reason about the epic and produce a decomposition. Consider:\n - Logical groupings of work that can be implemented and tested independently\n - Dependencies between sub-tasks (what must be built first)\n - A reasonable scope for each sub-task (each should be achievable in a single implementation session)\n\n3. Write the decomposition to `{docs_dir}/epic-plans/{epic_slug}/epic-plan.md` with this format:\n\n```markdown\n# Epic Decomposition\n\n## Sub-tasks\n\n### 1. {Sub-task title}\n- **Scope**: {What this sub-task covers}\n- **Key files/areas**: {Files and code areas involved}\n- **Dependencies**: {Other sub-task numbers this depends on, or \"None\"}\n\n### 2. {Sub-task title}\n...\n```\n\n4. **Soft limit check**: If the decomposition results in more than 8 sub-tasks, you must verbally warn the user: \"This decomposition has N sub-tasks, which exceeds the recommended limit of 8. Consider splitting this feature into multiple epics.\" Then proceed with the approval flow.\n\n5. Present the decomposition to the user and ask for their feedback. Explain the reasoning behind the breakdown and the dependency ordering.\n\n6. You MUST stop and wait for the user to respond. Do NOT assume approval. Do NOT proceed to the next step.\n\n7. If the user provides feedback or rejects the decomposition:\n - Incorporate their feedback\n - Rewrite `{docs_dir}/epic-plans/{epic_slug}/epic-plan.md` with the revised version\n - Present the revised decomposition and ask for approval again\n - Repeat until the user explicitly approves\n\n8. Only after explicit user approval, confirm: \"Decomposition approved. Proceeding to sub-task exploration.\"\n",
532
+ "evaluate-ticket-review.md": "Evaluate the clarifying questions and ticket critiques generated for {ticket_key}, assessing their accuracy and value against the actual codebase.\n\n1. Fetch the current ticket description using the `get_ticket` tool with ticket_number `{ticket_key}`.\n2. Gather all clarifying questions and critiques from the preceding pipeline steps. Both initial and second-opinion rounds may be present — use the MCP call results from the conversation context, since the files on disk at `{docs_dir}/clarifying-questions/{ticket_key}-clarifying-questions.md` and `{docs_dir}/ticket-critiques/{ticket_key}-ticket-quality-critique.md` only reflect the most recent round.\n3. **Read the Prior Work Assessment preamble (if present).** If the second-opinion documents begin with a `## Prior Work Assessment` section, extract the declared `Dominant failure mode` and `Effort allocation for this round`. If the delivered work materially diverges from the declared allocation (e.g., the model declared \"gap-heavy, ~75%\" but produced only 1 `**Prior Round**: Added` item across many `**Prior Round**: Kept` items, or declared \"correction-heavy\" but corrected almost nothing), record this divergence in a brief header block at the top of the evaluation output titled `**Second-Round Calibration Note**` with one sentence of detail. If no preamble exists or the delivered work matches the declared allocation, skip this note.\n4. For each clarifying question and its best-guess answer, verify groundedness against the codebase using file search and code grep. First, determine round agreement by scanning the second-opinion documents for inline `**Prior Round**` tags on each item:\n - **Both rounds agree**: The item appears in the second-opinion document tagged as `**Prior Round**: Kept` (kept substantially unchanged from the first round).\n - **Rounds disagree**: The item appears in the second-opinion document tagged as `**Prior Round**: Modified (...)` (materially changed from the first round), OR the item appears in the `### Removed Items` section tagged as `**Prior Round**: Removed (...)`.\n - **Added by second round**: The item is tagged as `**Prior Round**: Added` (new item the second round surfaced that was not in the first round). Apply the two-axis check below to classify it further.\n - **Single round only**: The pipeline ran only one round — no second-opinion document exists for this ticket.\n\n Apply the following depth and categorization rules:\n - **Both rounds agree**: Briefly confirm or refute with a single codebase citation. Keep the assessment to 1-2 sentences. Categorize as Confirmed Improvement if grounded, otherwise Needs Scrutiny.\n - **Rounds disagree**: Perform deeper analysis — cite 2+ codebase locations, explain the discrepancy, and state which round's position the evidence supports. Categorize based on which round prevails.\n - **Added by second round — two-axis check**:\n - If **both** the question is grounded in the codebase/standards **and** the best-guess answer is sensible against the codebase → categorize as **Confirmed Improvement** with a short (1-2 sentence) assessment and 1 citation. Set the Round Agreement field value to `gap captured`.\n - If the question is genuine but the **best-guess answer** is flawed → **Needs Scrutiny**. The Assessment must focus on why the answer misses and what the evidence supports instead. Cite 2+ files. Round Agreement value: `rounds disagree` (the reviewer still needs full evidence).\n - If the question itself does not hold up → **Needs Scrutiny** with evidence of what the code actually does. Round Agreement value: `rounds disagree`.\n - If neither codebase nor standards can settle the question → **Open Questions**. Round Agreement value: `rounds disagree`.\n - **Single round only**: Treat as a disagreement — cite 2+ codebase locations and give full analytical depth.\n\n Then categorize:\n - If the question/answer accurately reflects the codebase, confirm it as grounded with evidence.\n - If the question/answer misunderstands the codebase (wrong assumptions about architecture, incorrect file references, etc.), call it out with evidence of what the code actually does.\n - If the question raises a genuine gap that cannot be resolved from the codebase alone, note it as a legitimate open question.\n5. For each critique point (Requested Changes and Points to Consider), determine round agreement using the same `**Prior Round**` tag scanning method from step 4, then apply the same depth and categorization rules (including the two-axis check for `**Prior Round**: Added` items) and assess:\n - Does it identify a genuine gap or design issue in the ticket? Confirm with codebase evidence.\n - Is it based on an inaccurate understanding of the existing code? Flag with corrections.\n - Is it a stylistic preference rather than a substantive improvement? Note as low-priority.\n\n For `**Prior Round**: Added` critique items specifically: apply the two-axis check (grounded issue + sensible recommendation → Confirmed Improvement with Round Agreement value `gap captured`; flawed on either axis → Needs Scrutiny or Open Questions with Round Agreement value `rounds disagree`).\n6. Produce a structured evaluation with three sections, following the formatting rules below. Number each item sequentially across all sections (E-1, E-2, E-3, ...). If a `**Second-Round Calibration Note**` was generated in step 3, place it as a header block above the first section.\n\n## Writing Quality\n\nWrite as if explaining to a colleague who has NOT read the original clarifying questions or critique documents. Each assessment should be self-contained and understandable without cross-referencing the source material.\n\nStructure each **Assessment** using this three-point approach:\n1. **State the original suggestion**: What did the clarifying question or critique point propose?\n2. **State the codebase evidence**: What does the actual code show about this suggestion?\n3. **State the implication**: Does the evidence confirm the suggestion, contradict it, or leave it unresolved?\n\nThis structure ensures every assessment tells a complete story rather than assuming the reader already knows what was suggested and why.\n\n## Formatting Rules\n\nEach item must follow this template:\n\n```\n### E-<sequential number>: <concise title>\n\n**Source**: <which clarifying question or critique point this originated from, e.g. \"Clarifying Q3 (initial round)\" or \"Critique: Requested Change 2 (second opinion)\">\n\n**Round Agreement**: <\"both rounds agree\" | \"rounds disagree\" | \"gap captured\" | \"single round only\"> — <if disagreeing, 1 sentence on what the disagreement is; if \"gap captured\", 1 sentence noting what the second round surfaced>\n\n**Assessment**: <see depth calibration and writing quality rules>\n\n**Codebase Evidence**:\n- `path/to/file.ts:42` — <what this line/block demonstrates>\n- `path/to/other.ts:110-125` — <what this range demonstrates>\n\n<If no direct codebase evidence exists, state: \"No direct codebase evidence found.\">\n```\n\n**Depth calibration**:\n- When Round Agreement is `both rounds agree` or `gap captured`, keep Assessment to 1-2 sentences and Codebase Evidence to 1 citation — the second round (or both rounds) already did the heavy lifting.\n- When Round Agreement is `rounds disagree` or `single round only`, Assessment should be 3-4 sentences and Codebase Evidence should cite 2+ files explaining the discrepancy.\n- An `**Prior Round**: Added` item that FAILS the two-axis check uses the `rounds disagree` depth, not the `gap captured` depth, because the reviewer needs full evidence of where the issue or recommendation misses.\n\n## Example of a Well-Written E-Item\n\n### E-3: Missing retry logic for webhook delivery\n\n**Source**: Critique: Requested Change 1 (second opinion)\n\n**Round Agreement**: both rounds agree\n\n**Assessment**: Both rounds flagged that the ticket does not specify retry behavior when webhook delivery fails. The codebase already implements exponential backoff in the notification service (`src/services/notifications.py:87-102`), confirming that retry logic is an established pattern and the ticket should explicitly require it for consistency.\n\n**Codebase Evidence**:\n- `src/services/notifications.py:87-102` — existing `retry_with_backoff()` helper uses exponential backoff with a base of 2 seconds and max 3 retries for outbound HTTP calls\n\n## Example of a Well-Written E-Item (Rounds Disagree)\n\n### E-5: Authentication middleware placement for new endpoint\n\n**Source**: Clarifying Q2 (initial round)\n\n**Round Agreement**: rounds disagree — Round 1 recommended adding auth at the router level; Round 2 argued the existing middleware stack already covers it.\n\n**Assessment**: The initial round suggested that the new `/api/exports` endpoint needs an explicit `Depends(require_api_key)` guard because it is not covered by the global middleware. The second round disagreed, claiming the existing middleware stack in `main.py` handles authentication for all `/api/*` routes. Codebase analysis shows that `main.py:45-52` applies rate limiting globally but authentication is applied per-router in `api/routes/__init__.py:18-30` — each router must opt in via `Depends(require_api_key)`. This supports the first round's position: the new endpoint needs an explicit auth dependency.\n\n**Codebase Evidence**:\n- `main.py:45-52` — global middleware applies rate limiting and CORS, but not authentication\n- `api/routes/__init__.py:18-30` — each router includes its own auth dependency; there is no catch-all auth middleware\n\n## Example of a Well-Written E-Item (Gap Captured)\n\n### E-7: Missing Alembic migration for new role-scope column\n\n**Source**: Critique: Requested Change 4 (second opinion, **Prior Round**: Added)\n\n**Round Agreement**: gap captured — the second round flagged a missing migration that the initial round did not raise, and recommended adding an Alembic revision.\n\n**Assessment**: The ticket introduces a new `role_scope` column on the `users` table but does not mention a migration. The second round flagged this gap and recommended adding an Alembic revision; both the gap and the recommendation are grounded, since `db/alembic/versions/` is the established location for schema changes per the project's database guide.\n\n**Codebase Evidence**:\n- `db/alembic/versions/` — all schema changes land here as autogenerated revisions\n\n## Output Sections\n\n- **Confirmed Improvements**: Suggestions that are grounded and would genuinely improve the ticket by closing significant gaps or correcting design issues. Includes `gap captured` items (sound `**Prior Round**: Added` issue + sound recommendation).\n- **Needs Scrutiny**: Suggestions based on inaccurate codebase assumptions, with evidence of the actual code behavior. Includes `**Prior Round**: Added` items that failed either axis of the two-axis check.\n- **Open Questions**: Legitimate ambiguities that require human input to resolve.\n\n7. Save the evaluation to `{docs_dir}/review/{ticket_key}-review-evaluation.md`. Output only the structured evaluation (including the `**Second-Round Calibration Note**` header if present) — no meta-commentary.\n",
509
533
  "execute-epic-research.md": "Execute the research plan and write findings.\n\n## Instructions\n\n1. Read the research plan from `{docs_dir}/epic-plans/{epic_slug}/research-plan.md`.\n\n2. Execute the plan based on the Research Mode:\n\n **If mode is `deep`**:\n - Call the `request_deep_research` MCP tool with:\n - `query`: the Deep Research Query from the plan\n - `context`: \"Bridge API is a Python/FastAPI application with PostgreSQL, LiteLLM, and Pinecone. This research supports epic planning for: {epic_description}\"\n - `wait_for_result`: true\n - `save_locally`: true\n - If deep research fails, log a warning and fall back to web searches using the Web Search Topics from the plan. Do NOT halt.\n\n **If mode is `web`**:\n - Perform web searches for each topic listed in the plan.\n - Capture relevant findings from each search.\n\n **If mode is `none`**:\n - Write a brief note: \"No external research needed. Proceeding with codebase exploration.\"\n\n3. Write all findings to `{docs_dir}/epic-plans/{epic_slug}/research-findings.md` with this structure:\n\n```markdown\n# Research Findings\n\n## Mode\n{deep | web | none}\n\n## Findings\n{Synthesized research results organized by topic. Include source references where applicable.}\n\n## Key Takeaways\n{Bullet points summarizing the most important findings that will inform the codebase exploration and epic decomposition.}\n```\n",
510
- "execute-plan.md": "Execute the AI-generated implementation plan for ticket {ticket_key}.\n\n1. Read the plan from `{docs_dir}/plans/{ticket_key}-plan.md`.\n2. Execute each step in the plan sequentially, making code changes as directed.\n3. Run any tests or checks specified in the plan's review steps.\n4. Do NOT run `git commit` or `git push` — leave all changes uncommitted for developer review.\n5. If a step is ambiguous or blocked, note the issue clearly and continue with the next step.\n",
534
+ "execute-plan.md": "Execute the AI-generated implementation plan for ticket {ticket_key}.\n\n---\n\n## Step 1 — Load and Enumerate the Plan\n\n1. Read the plan from `{docs_dir}/plans/{ticket_key}-plan.md`.\n2. Count the total number of implementation steps in the plan.\n3. Announce: **\"Plan contains N steps.\"**\n\n## Step 2 — Execute Each Step Sequentially\n\nFor each step in the plan:\n\n1. **Announce** before starting: **\"Step X of N: <step title from plan>\"**\n2. **Execute** the step, making code changes as directed.\n3. **Confirm** after completing: **\"Step X complete — <brief summary of what was done>.\"**\n\n### Rules\n\n- Execute steps in strict sequential order. Do not skip, reorder, or combine steps.\n- Run any tests or checks specified in the plan's review steps.\n- Do NOT run `git commit` or `git push` — leave all changes uncommitted for developer review.\n- If a step is ambiguous or blocked, note the issue clearly (what is ambiguous and why) and continue with the next step.\n\n## Step 3 — Final Audit\n\nAfter all steps are executed:\n\n1. Re-read the plan file at `{docs_dir}/plans/{ticket_key}-plan.md`.\n2. Compare the plan against the work completed. Verify every step was addressed.\n3. List any steps that were skipped or only partially completed, with reasons.\n4. Announce: **\"Audit complete — N of N steps fully addressed.\"** (or note discrepancies).\n",
511
535
  "explore-epic-codebase.md": "Perform a holistic, epic-level codebase exploration.\n\n## Epic Description\n\n{epic_description}\n\n## Instructions\n\n1. Read the research findings from `{docs_dir}/epic-plans/{epic_slug}/research-findings.md` to establish context. If the file does not exist or is empty, proceed without it.\n\n2. Explore the codebase with a focus on breadth rather than depth. The goal is to build a \"lay of the land\" understanding for the entire epic, not to deeply analyze any single sub-task. Use `Glob`, `Grep`, and `Read` tools to search for:\n - Files, modules, and directories relevant to the epic\n - Architectural patterns used in similar features\n - Integration points and dependencies between modules\n - Existing conventions for the type of work this epic involves\n - Database models, API routes, agent flows, and utilities that may be affected\n\n3. Build a mental model of:\n - What exists today that relates to the epic\n - What patterns and conventions are used in similar features\n - What dependencies, data flows, and integration points are involved\n - What areas of the codebase will likely need changes\n\n4. Write the exploration findings to `{docs_dir}/epic-plans/{epic_slug}/codebase-exploration.md` with this structure:\n\n```markdown\n# Codebase Exploration\n\n## Architecture Overview\n{High-level description of how the relevant parts of the codebase are structured.}\n\n## Relevant Code Areas\n{List of key files, modules, and directories with brief descriptions of their relevance to the epic.}\n\n## Existing Patterns\n{Patterns and conventions discovered that should be followed when implementing the epic.}\n\n## Integration Points\n{Dependencies, data flows, and integration points that the epic will need to account for.}\n\n## Potential Challenges\n{Any architectural constraints, technical debt, or complexity that could affect implementation.}\n```\n",
512
- "explore-epic-subtasks.md": "Perform focused code explorations for each approved sub-task.\n\n## Instructions\n\n1. Read the approved decomposition from `{docs_dir}/epic-plans/{epic_slug}/decomposition.md`.\n\n2. Create the explorations directory:\n ```\n mkdir -p {docs_dir}/epic-plans/{epic_slug}/explorations/\n ```\n\n3. For each sub-task in the decomposition, perform a focused exploration:\n - Search for specific files and patterns relevant to the sub-task\n - Identify implementation options and tradeoffs\n - Reference the holistic codebase exploration (`{docs_dir}/epic-plans/{epic_slug}/codebase-exploration.md`) and research findings (`{docs_dir}/epic-plans/{epic_slug}/research-findings.md`) for context\n - Default to lightweight exploration — only go deeper when the holistic exploration left significant gaps for a specific sub-task\n\n4. Write an exploration document for each sub-task to `{docs_dir}/epic-plans/{epic_slug}/explorations/NN-{subtask-slug}.md` (using zero-padded numbering, e.g., `01-add-pipeline-json.md`, `02-create-instruction-files.md`).\n\n5. Each exploration document MUST include these exactly named sections:\n\n```markdown\n# {Sub-task title}\n\n## Context\n{Brief description of the sub-task scope and its role within the epic.}\n\n## Relevant Code\n{Specific files, functions, and patterns relevant to this sub-task. Reference with file_path:line_number format.}\n\n## Implementation Options\n{Viable approaches for implementing the sub-task. For each option: description, pros, cons.}\n\n## Recommendation\n{Which option to pursue and why. Include any caveats or risks.}\n```\n\n6. **Word count guidance**: Target 300-500 words per document. Keep the exploration lightweight. Only exceed this limit if the holistic codebase exploration left significant gaps for a specific sub-task.\n",
536
+ "explore-epic-subtasks.md": "Perform focused code explorations for each approved sub-task.\n\n## Instructions\n\n1. Read the approved decomposition from `{docs_dir}/epic-plans/{epic_slug}/epic-plan.md`.\n\n2. Create the explorations directory:\n ```\n mkdir -p {docs_dir}/epic-plans/{epic_slug}/explorations/\n ```\n\n3. For each sub-task in the decomposition, perform a focused exploration:\n - Search for specific files and patterns relevant to the sub-task\n - Identify implementation options and tradeoffs\n - Reference the holistic codebase exploration (`{docs_dir}/epic-plans/{epic_slug}/codebase-exploration.md`) and research findings (`{docs_dir}/epic-plans/{epic_slug}/research-findings.md`) for context\n - Default to lightweight exploration — only go deeper when the holistic exploration left significant gaps for a specific sub-task\n\n4. Write an exploration document for each sub-task to `{docs_dir}/epic-plans/{epic_slug}/explorations/NN-{subtask-slug}.md` (using zero-padded numbering, e.g., `01-add-pipeline-json.md`, `02-create-instruction-files.md`).\n\n5. Each exploration document MUST include these exactly named sections:\n\n```markdown\n# {Sub-task title}\n\n## Context\n{Brief description of the sub-task scope and its role within the epic.}\n\n## Relevant Code\n{Specific files, functions, and patterns relevant to this sub-task. Reference with file_path:line_number format.}\n\n## Implementation Options\n{Viable approaches for implementing the sub-task. For each option: description, pros, cons.}\n\n## Recommendation\n{Which option to pursue and why. Include any caveats or risks.}\n```\n\n6. **Word count guidance**: Target 300-500 words per document. Keep the exploration lightweight. Only exceed this limit if the holistic codebase exploration left significant gaps for a specific sub-task.\n",
513
537
  "learn-architecture.md": "## Objective\n\nExplore the codebase to identify architectural principles, directory conventions, design patterns, and data flow, then draft `architecture_instructions` for the project config.\n\n## Instructions\n\n### Phase 1 — Principles Research\n\nResearch the codebase to identify architectural principles and conventions. For each area below, examine at least 5 representative files. Cite file paths for every pattern. Include code examples (5-15 lines) showing correct usage. Where relevant, include a WRONG example showing the common mistake.\n\nFor each pattern, classify its evidence level:\n- `ENFORCED` — consistently followed across the codebase, violations would be bugs\n- `CONVENTION` — commonly observed, occasional deviations exist\n- `ASPIRATIONAL` — intended direction, not yet consistently applied\n\nResearch areas:\n1. **Architectural coding patterns**: Search `api/routes/` and `api/library/` for separation of concerns, layer boundaries, function-vs-class decisions. Read files matching `*_lib.py`, `*_utils.py`, `*_helpers.py` to document module naming suffix conventions.\n2. **Design patterns**: Search for factory functions, strategy patterns, middleware chains, registry patterns, and dependency injection in `api/` and `src/python/`. Cite concrete usage with file path and function name.\n3. **Dependency management**: Read `requirements.in`, `requirements-dev.in`, and `package.json` files to document how dependencies are declared and organized.\n4. **Error handling architecture**: Search for `log_exception_to_sentry` and `HTTPException` usage patterns across `api/routes/` to document the system-wide error propagation strategy.\n5. **Configuration management**: Search for `os.environ` and `get_config_field` usage to document the two-tier system (env vars vs. database config).\n6. **Tech stack detection**: Read `requirements.in`, `package.json`, and `main.py` to identify primary languages, frameworks, and key libraries.\n7. **Security architecture**: Read `api/routes/setup/auth.py` and search for `require_api_key`, `require_api_session`, and `verify_repo_access` to document authentication and authorization design.\n8. **Agent prompting conventions**: Read files in `src/python/llms/agents/` to document prompt construction, section headers, dynamic content delimiters, and role-based personas.\n\nScope exclusion: Do NOT document testing patterns. Skip the `tests/` directory entirely.\n\nWrite findings to `{docs_dir}/tmp/architecture-principles.md`.\n\n### Phase 2 — Structure & Data Flow Research\n\n1. Call the `regenerate_directory_map` MCP tool to get a fresh directory map.\n2. Read the principles document from Phase 1.\n3. Research and document:\n - **Directory conventions**: For each major directory, document purpose, file naming, internal structure, and an example file.\n - **Module boundaries and import patterns**: Which directories are distinct modules and how they interact. Document import restrictions.\n - **Data flow patterns**: Trace 2-3 complete request paths (synchronous, async background task, agent orchestration).\n - **Integration patterns**: How external services (Jira, GitHub/Bitbucket, LLMs, Pinecone, PostgreSQL) are integrated.\n - **Background task patterns**: The async task lifecycle with `asyncio.create_task`, semaphores, and error reporting.\n\nWrite findings to `{docs_dir}/tmp/architecture-structure.md`.\n\n### Phase 3 — Draft\n\n1. Read both research documents.\n2. Combine into a single `architecture_instructions` draft with these required sections:\n - **1. Core Principles** — Each principle with evidence level and explanation.\n - **2. Layered Architecture** — Layer separation, dependency rule, agent vs orchestration logic.\n - **3. Directory Conventions** — Purpose, naming, structure for each major directory.\n - **4. Data Flow Patterns** — Complete request path traces with file paths.\n - **5. Technical Standards** — Coding style, async patterns, database, schema, LLM integration, config, dependencies.\n - **6. Error Handling & Monitoring** — Error propagation strategy, Sentry integration, Langfuse tracing.\n - **7. Security & Authentication** — Auth architecture, session model, permission model.\n - **8. Agent Prompting Conventions** — Prompt construction, section headers, content delimiters.\n - **9. Integration Points** — External service clients and their calling patterns.\n - **10. AI Code Generation Guidelines** — Anti-patterns, duplication avoidance, pattern compliance checklist.\n\n3. Write the draft to `{docs_dir}/standards/architecture_instructions.md`.\n",
514
538
  "learn-backend-correctness.md": "## Objective\n\nExplore the codebase to identify correctness standards for backend code, then draft the corresponding correctness standards document.\n\n## Target Type\n\n- **Type**: `backend_correctness`\n- **Field name**: `backend_correctness_standards`\n- **Scope**: Server-side code: Python, Ruby, Go, Java, C#, Node.js server code, API routes, business logic.\n\n## Instructions\n\n### Phase 1 — Explore Correctness Patterns\n\nFocus on implementation correctness: how to write code that is correct, idiomatic, and robust within this project's conventions.\n\n1. **File Type Detection**: Use Glob to find files matching `**/*.py` in `api/` and `src/python/`. If very few or no files exist, note this and draft minimal instructions.\n\n2. **Convention Analysis**: Read 3-5 representative files in `api/routes/` and `api/library/` to identify:\n - Structure patterns (imports, exports, class structure, function ordering)\n - Naming conventions (variables, functions, classes, files)\n - Framework conventions and idioms\n - Best practices followed\n - Issues and inconsistencies\n\n Also read files to document:\n - Error handling implementation (try/except ordering, Sentry calls) with CORRECT/WRONG examples\n - Authentication implementation (auth check sequence) with code examples\n - Database call patterns (`postgres_helpers` (bool, result) tuple handling) with CORRECT/WRONG examples\n - Input validation patterns (Pydantic models, naming conventions)\n - HTTP client patterns (error handling, JiraError sanitization)\n - Async implementation patterns (`asyncio.to_thread()` for blocking code)\n\n### Phase 2 — Draft\n\nDraft correctness standards as clear, actionable instructions for an AI code generation agent. Cover:\n- Code structure and organization requirements\n- Naming conventions to follow\n- Framework-specific patterns and idioms\n- Security requirements relevant to this code type\n- Performance considerations\n- Common mistakes to avoid\n- Guards against common AI weaknesses: duplicative code, verbose implementations, security vulnerabilities\n\nAlso include:\n- Route handler boilerplate (auth -> validation -> business logic -> error handling)\n- Database interaction patterns with CORRECT/WRONG examples\n- Exception handling pattern (specific first, HTTPException re-raise, generic with Sentry)\n- Sentry reporting patterns and common mistakes\n- Input sanitization rules (JiraError headers, raw exception messages)\n\nWrite the draft to `{docs_dir}/standards/backend_correctness_standards.md`.\n",
539
+ "learn-design-principles.md": "## Objective\n\nExplore the codebase to identify frontend design principles, then draft a structured design principles document.\n\n## Target Type\n\n- **Type**: `design_principles`\n- **Field name**: `design_principles`\n- **Scope**: Visual identity, design tokens, component inventory, layout patterns, composition rules, interaction patterns, and anti-patterns.\n\n## Instructions\n\n### Phase 1 — Explore Design Patterns\n\nUse **Glob**, **Grep**, and **Read** to explore the codebase for design patterns. **Exclude `node_modules/`, `dist/`, `build/`, `.next/`, and `__pycache__/` from all Glob searches** to avoid token bloat.\n\nCall the `get_project_standards` MCP tool to check if `working_in` and `version` context is available. If available, use them to prioritize relevant file types. If unavailable or the call fails, read dependency files (`package.json`, `tailwind.config.js`, `postcss.config.js`) to infer the framework and styling approach.\n\n1. **Design Token Detection**: Search for CSS custom properties, SCSS/LESS variables, theme configs, Tailwind config, and design token definitions. Document naming conventions, token hierarchy, and value scales (spacing, colors, typography).\n\n2. **Component Inventory**: Use Glob to find component files (JSX/TSX/Vue/Svelte/ISML/template files). Read 5-10 representative components to identify composition patterns, prop interfaces, naming conventions, and component categories.\n\n3. **Style Architecture**: Find and analyze stylesheets (CSS/SCSS/LESS/styled-components/Tailwind). Document methodology (BEM, CSS Modules, utility-first), responsive breakpoints, and media query patterns.\n\n4. **Layout Patterns**: Identify grid systems, page templates, container components, and responsive layout strategies.\n\n5. **Interaction Patterns**: Search for animations, transitions, hover states, loading states, and error states.\n\n6. **Visual Consistency Audit**: Compare patterns across files. Note inconsistencies in spacing, color usage, component structure, or naming.\n\n### Phase 2 — Draft\n\nSynthesize findings into a structured document with exactly these 7 sections:\n\n1. **Visual Identity** — Colors, typography, spacing scales, iconography, visual tone\n2. **Design Token Reference** — Token naming conventions, hierarchy, value definitions\n3. **Component Inventory** — What components exist, their responsibilities, naming patterns\n4. **Page Layout Patterns** — Grid systems, page templates, responsive strategies, container patterns\n5. **Composition Rules** — How components combine, nesting patterns, slot/children conventions\n6. **Interaction Patterns** — Animations, transitions, states, hover/focus/active behaviors\n7. **Anti-patterns** — Inconsistencies found, patterns to avoid, deprecated approaches\n\nWrite the draft to `{docs_dir}/standards/design_principles.md`.\n",
515
540
  "learn-documentation-instructions.md": "## Objective\n\nExplore the codebase to identify implementation documentation patterns — the markdown records that document what was built, why, and when — then draft `documentation_instructions` for the project config.\n\n## Instructions\n\n### Phase 1 — Explore Implementation Record Patterns\n\nFocus on how the project records what was built, why, and when. These records serve as persistent project memory. Code-level documentation (docstrings, inline comments) is handled by correctness standards, not here.\n\n1. **Implementation Record Discovery**: Search for:\n - Ticket-numbered documents matching `BAPI-*.md` or `PROJ-*.md` in `docs/` and subdirectories\n - Feature/migration documents in `docs/`, `documentation/`, or similar directories\n - Architecture Decision Records (ADRs) in `adr/`, `decisions/`, or similar\n - Changelogs (`CHANGELOG.md`, release notes)\n\n Count how many records exist and identify the naming convention.\n\n2. **Record Structure Analysis**: Read 3-5 representative implementation records (mix of early and recent). Document:\n - Sections present (Summary, Architecture, Database Changes, API Reference, etc.)\n - Level of detail provided\n - Types of information captured (motivation, design decisions, schema changes, file paths, API contracts)\n - How code examples and diagrams are used\n\n3. **Documentation Location and Organization**: Read the directory structure of `docs/` to identify where records are stored, the file naming convention, whether there is a table of contents or index, and whether subdirectories serve different purposes.\n\n### Phase 2 — Draft\n\nDraft `documentation_instructions` as clear, actionable instructions for an AI agent writing implementation documentation after completing a feature. Cover:\n- **Purpose**: Documents serve as persistent project memory for future agents and developers.\n- **When to write**: After completing any feature, refactor, migration, or significant bug fix.\n- **File naming convention**: Based on discovered patterns or sensible defaults.\n- **File location**: Where to place implementation records.\n- **Required sections**: Standard structure from analysis or sensible defaults.\n- **Content guidelines**: Level of detail — motivations, design decisions, database changes, file references, API contracts.\n- **Scope boundary**: Implementation records only; code-level docs belong in correctness standards.\n\nWrite the draft to `{docs_dir}/standards/documentation_instructions.md`.\n",
516
541
  "learn-e2e-testing.md": "## Objective\n\nDetect whether an E2E testing framework exists in the codebase, document how to run and write E2E tests, then draft `e2e_testing_instructions` for the project config.\n\n## Instructions\n\n### Phase 1 — Detect E2E Framework\n\nSearch for config files and indicators:\n- **Playwright**: Search for `playwright.config.ts`, `playwright.config.js`, `@playwright/test` in `package.json`\n- **Cypress**: Search for `cypress.json`, `cypress.config.*`, `cypress/` directory\n- **Selenium/WebDriver**: Search for `selenium` in `requirements.in` or `package.json`\n- **Puppeteer**: Search for `puppeteer` in `package.json`\n- **TestCafe**: Search for `.testcaferc.json`\n\nAlso read `package.json` for E2E-related scripts and search for test directories containing E2E tests.\n\nIf NO E2E testing framework is detected, write \"No E2E testing framework detected in this repository.\" to `{docs_dir}/standards/e2e_testing_instructions.md` and stop.\n\n### Phase 2 — Explore E2E Testing Conventions\n\n1. **Test Execution**: Read the E2E config file and `package.json` scripts to determine exact commands (all tests, single file, headed/headless), prerequisites (server running, database seeded), and environment requirements.\n\n2. **Test Patterns**: Read 2-3 representative E2E test files in `tests/playwright/` to identify structure (page objects, fixtures, helpers), login/auth flows, test data setup/teardown, async waiting strategies, and selector patterns.\n\n3. **Common Pitfalls**: Search for hard-coded waits (`setTimeout`, `page.waitForTimeout`), test isolation issues, and browser state management patterns across E2E test files.\n\n### Phase 3 — Draft\n\nDraft `e2e_testing_instructions` as clear, actionable instructions for an AI agent writing E2E tests. Cover:\n- How to run tests (exact commands, prerequisites)\n- Test structure and organization\n- Authentication and setup patterns\n- How to wait for async operations (never hard-coded sleeps)\n- Common pitfalls with browser automation\n- Guards against common AI weaknesses: flaky tests, brittle selectors, hard-coded waits\n\nWrite the draft to `{docs_dir}/standards/e2e_testing_instructions.md`.\n",
517
542
  "learn-frontend-correctness.md": "## Objective\n\nExplore the codebase to identify correctness standards for frontend code, then draft the corresponding correctness standards document.\n\n## Target Type\n\n- **Type**: `frontend_correctness`\n- **Field name**: `frontend_correctness_standards`\n- **Scope**: JS, TS, JSX, TSX files: React/Vue/Angular/Svelte components, client-side logic, state management.\n\n## Instructions\n\n### Phase 1 — Explore Correctness Patterns\n\nFocus on implementation correctness: how to write code that is correct, idiomatic, and robust within this project's conventions.\n\n1. **File Type Detection**: Use Glob to find files matching `**/*.js`, `**/*.ts`, `**/*.jsx`, `**/*.tsx` (excluding `node_modules/` and `build/`). If very few or no files exist, note this and draft minimal instructions.\n\n2. **Convention Analysis**: Read 3-5 representative frontend files to identify:\n - Structure patterns (imports, exports, class structure, function ordering)\n - Naming conventions (variables, functions, classes, files)\n - Framework conventions and idioms\n - Best practices followed\n - Issues and inconsistencies\n\n### Phase 2 — Draft\n\nDraft correctness standards as clear, actionable instructions for an AI code generation agent. Cover:\n- Code structure and organization requirements\n- Naming conventions to follow\n- Framework-specific patterns and idioms\n- Security requirements relevant to this code type\n- Performance considerations\n- Common mistakes to avoid\n- Guards against common AI weaknesses: duplicative code, verbose implementations, security vulnerabilities\n\nWrite the draft to `{docs_dir}/standards/frontend_correctness_standards.md`.\n",
@@ -520,7 +545,7 @@ export const INSTRUCTIONS = {
520
545
  "learn-template-correctness.md": "## Objective\n\nExplore the codebase to identify correctness standards for template files, then draft the corresponding correctness standards document.\n\n## Target Type\n\n- **Type**: `template_correctness`\n- **Field name**: `template_correctness_standards`\n- **Scope**: Template files: HTML, Jinja2, Handlebars, EJS, ERB, Blade, Pug, Twig.\n\n## Instructions\n\n### Phase 1 — Explore Correctness Patterns\n\nFocus on implementation correctness: how to write code that is correct, idiomatic, and robust within this project's conventions.\n\n1. **File Type Detection**: Use Glob to find files matching `**/*.html`, `**/*.jinja2`, `**/*.j2` in `templates/` and similar directories (excluding `node_modules/`). If very few or no files exist, note this and draft minimal instructions.\n\n2. **Convention Analysis**: Read 3-5 representative template files to identify:\n - Structure patterns (imports, exports, class structure, function ordering)\n - Naming conventions (variables, functions, classes, files)\n - Framework conventions and idioms\n - Best practices followed\n - Issues and inconsistencies\n\n### Phase 2 — Draft\n\nDraft correctness standards as clear, actionable instructions for an AI code generation agent. Cover:\n- Code structure and organization requirements\n- Naming conventions to follow\n- Framework-specific patterns and idioms\n- Security requirements relevant to this code type\n- Performance considerations\n- Common mistakes to avoid\n- Guards against common AI weaknesses: duplicative code, verbose implementations, security vulnerabilities\n\nWrite the draft to `{docs_dir}/standards/template_correctness_standards.md`.\n",
521
546
  "learn-unit-testing.md": "## Objective\n\nExplore the codebase to identify the test runner, assertion library, mocking framework, and testing patterns, then draft `unit_testing_instructions` for the project config.\n\n## Instructions\n\n### Phase 1 — Explore Testing Infrastructure\n\n1. **Test Runner and Framework Detection**: Search for test runner configs (`pytest.ini`, `pyproject.toml` `[tool.pytest]` section, `jest.config.*`) and read `package.json` test scripts. Read the `tests/` directory structure.\n\n2. **Testing Patterns**: Read 3-5 representative test files in `tests/pytest/` to identify:\n - Assertion library and style (`assert`, `expect`, custom matchers)\n - Mocking framework (`unittest.mock`, `jest.mock`, `sinon`, etc.)\n - Fixture patterns (setup/teardown)\n - Test organization (by module, feature, layer)\n - Exemplary tests vs. weak tests\n\n3. **How to Run Tests**: Read `pyproject.toml`, `package.json`, and `Makefile` (if present) to determine exact commands for: full suite, single file, by name pattern, with verbose output.\n\n4. **Mocking vs. Fidelity**: Read test helper files in `tests/pytest/helpers/` to document how external APIs are mocked, whether integration tests exist alongside unit tests, and patterns for avoiding third-party calls in tests.\n\n### Phase 2 — Draft\n\nDraft `unit_testing_instructions` as clear, actionable instructions for an AI agent writing unit tests. Cover:\n- How to run tests (exact commands)\n- Which test framework and assertion library to use\n- How to mock external dependencies without calling third parties\n- How to structure test files and test functions\n- What constitutes a thorough test (not just happy path)\n- How to avoid shallow tests that pass but don't verify meaningful behavior\n- Guards against common AI weaknesses: tests that mock the thing being tested, trivially passing assertions, overly complex setup\n\nWrite the draft to `{docs_dir}/standards/unit_testing_instructions.md`.\n",
522
547
  "monitor-ci-checks.md": "Monitor CI checks for the most recent commit.\n\n1. Run `git rev-parse HEAD` to get the current commit SHA.\n2. Call the `resolve_ci_checks` tool with the commit SHA as `commit_ref`. This discovers and classifies the CI checks for the repository.\n3. Poll CI status by calling `poll_ci_checks` with the same `commit_ref`. Check the response for `all_complete` and `all_passed`.\n4. If checks are not yet complete, wait 30 seconds and poll again. Repeat until all checks are complete or 10 minutes have elapsed.\n5. If all checks pass, report success.\n6. If any checks fail, report which checks failed and include any available annotations or log details from the poll response. Do NOT attempt to fix failures — just report them clearly.\n\n## Polling Directive\n\nDuring the polling loop, execute `sleep 30` silently. Do NOT output any inline commentary, reasoning, or partial status updates between polls. Only output a status message when:\n- All checks are complete (pass or fail), OR\n- The 10-minute timeout is reached.\n\nThis minimizes context window consumption during long-running CI waits.\n",
523
- "recommend-ticket-resolutions.md": "Read the evaluation produced by the previous pipeline step and generate a resolution guide for {ticket_key}.\n\n1. Read the evaluation from `{docs_dir}/review/{ticket_key}-review-evaluation.md`.\n2. Fetch the current ticket description using the `get_ticket` tool with ticket_number `{ticket_key}` for context.\n3. Before processing individual items, produce the following executive summary at the top of the output file:\n\n## TL;DR: Items Needing Your Decision\n\nList every item from the Needs Scrutiny and Open Questions sections of the evaluation, one bullet each:\n\n- **E-<number>: <title>** — <1-sentence summary of what decision is needed>\n\nIf both sections are empty, write: \"No decisions needed — all suggestions were confirmed as improvements.\"\n\n4. Process each item from the evaluation according to its section, following the rules below.\n\n## Needs Scrutiny\n\nFor each item in the Needs Scrutiny section, produce:\n\n```\n### E-<number>: <title from evaluation>\n\n**Confidence**: <High/Medium/Low>\n**Resolution path**: <\"resolve at your desk\" or \"needs a conversation\">\n\n**Decision tree**:\n- If <condition 1>, then <action 1>. See `file:line`. <1-2 sentence rationale.>\n- If <condition 2>, then <action 2>. See `file:line`. <1-2 sentence rationale.>\n- If <condition 3>, then <action 3>. See `file:line`. <1-2 sentence rationale.>\n\n**Recommendation Index**: <0-based index of the recommended branch in the decision tree above>\n**Recommendation**: <which branch the evidence best supports and why, 1-2 sentences>\n```\n\n## Open Questions\n\nFor each item in the Open Questions section, produce:\n\n```\n### E-<number>: <title from evaluation>\n\n**Confidence**: <High/Medium/Low>\n**Resolution path**: <\"resolve at your desk\" or \"needs a conversation\">\n\n**Decision tree**:\n- If <condition 1>, then <action 1>. See `file:line` (if available). <1-2 sentence rationale.>\n- If <condition 2>, then <action 2>. See `file:line` (if available). <1-2 sentence rationale.>\n\n**Recommendation Index**: <0-based index of the recommended branch in the decision tree above>\n**Recommendation**: <best guess at resolution path and why, or \"Insufficient evidence — escalate to <suggested stakeholder role>\", 1-2 sentences>\n```\n\n## Confirmed Improvements\n\nRender each Confirmed Improvement as a single bullet in a compact list. No headings per item, no decision trees, no rationale paragraphs.\n\n- **E-<number>: <title>** (Confidence: <High/Medium/Low>) — <recommended action, 1 sentence>\n\n## Round Agreement Summary\n\nAfter all items are processed, produce a summary section that groups items by round agreement status from the evaluation:\n\n### Points of Disagreement\nFor items where the evaluation marked \"rounds disagree\" or \"single round only\": list as bullets with the E-number, the nature of the disagreement, and a 1-sentence explanation of why this disagreement matters for the ticket (e.g., it indicates an architectural ambiguity, a scope question, or a standards gap).\n\nIf no items were marked as disagreements, write: \"All reviewed points had round consensus. No disagreement-driven risks identified.\"\n\n### Points of Agreement\nFor items where the evaluation marked \"both rounds agree\": list as bullets with the E-number and a half-sentence noting the shared conclusion. These items required minimal analysis and are lower risk.\n\n## Decision Tree Rules\n\n- Each decision tree must have 2-4 branches. Do not exceed 4.\n- Each branch must end with a concrete, actionable step (not \"investigate further\").\n- Cite relevant code in `file:line` format where possible. If no code reference exists, omit the citation rather than fabricating one.\n- Cap each branch at 2-3 sentences total (including the action and rationale).\n- `**Recommendation Index**` must be the 0-based index of the recommended branch in the decision tree above. The first branch is index 0, the second is index 1, etc.\n- **\"resolve at your desk\"**: The item can be resolved through technical investigation — reading code, running tests, or checking configuration. No stakeholder input needed.\n- **\"needs a conversation\"**: The item involves a product decision, scope question, or cross-team dependency that cannot be resolved from the codebase alone.\n\n## Confidence Tags\n\nAssign confidence based on codebase evidence strength from the evaluation:\n- **High**: The evaluation cites specific `file:line` references that directly support the assessment.\n- **Medium**: The evaluation references related code patterns or architectural conventions, but not the exact code in question.\n- **Low**: No direct codebase evidence. Assessment is based on general reasoning or domain knowledge.\n\n## Edge Cases\n\n- If the evaluation contains zero items in Needs Scrutiny, write: \"No items flagged for scrutiny. All reviewed suggestions were either confirmed or remain open questions.\"\n- If the evaluation contains zero items in Open Questions, write: \"No open questions identified. All ambiguities were resolved through codebase analysis.\"\n- If both Needs Scrutiny and Open Questions are empty, include only the Confirmed Improvements section and add a summary: \"All suggestions from the review were confirmed as grounded improvements. No decision trees are needed.\"\n- If the evaluation does not contain Round Agreement fields (e.g., it was generated before this format), infer agreement status from the Source field: if both rounds are cited with the same conclusion, treat as agreement; if only one round is cited or they conflict, treat as disagreement.\n\n5. Save the resolution guide to `{docs_dir}/review/{ticket_key}-resolution-guide.md`. Output only the resolution guide — no meta-commentary.\n",
548
+ "recommend-ticket-resolutions.md": "Read the evaluation produced by the previous pipeline step and generate a resolution guide for {ticket_key}.\n\n1. Read the evaluation from `{docs_dir}/review/{ticket_key}-review-evaluation.md`.\n2. Fetch the current ticket description using the `get_ticket` tool with ticket_number `{ticket_key}` for context.\n3. Before processing individual items, produce the following executive summary at the top of the output file:\n\n## TL;DR: Items Needing Your Decision\n\nIf the evaluation contains a `**Second-Round Calibration Note**` header, or if the second-opinion document's Prior Work Assessment section is available, surface one line immediately under the `## TL;DR` heading:\n\n`**Prior Work Assessment**: <dominant failure mode from the second-opinion document>` — so the reviewer knows which direction the second round leaned.\n\nThen list every item from the Needs Scrutiny and Open Questions sections of the evaluation, one bullet each:\n\n- **E-<number>: <title>** — <1-sentence summary of what decision is needed>\n\nIf both sections are empty, write: \"No decisions needed — all suggestions were confirmed as improvements.\"\n\n4. Process each item from the evaluation according to its section, following the rules below.\n\n## Needs Scrutiny\n\nFor each item in the Needs Scrutiny section, produce:\n\n```\n### E-<number>: <title from evaluation>\n\n**Confidence**: <High/Medium/Low>\n**Resolution path**: <\"resolve at your desk\" or \"needs a conversation\">\n\n**Decision tree**:\n- If <condition 1>, then <action 1>. See `file:line`. <1-2 sentence rationale.>\n- If <condition 2>, then <action 2>. See `file:line`. <1-2 sentence rationale.>\n- If <condition 3>, then <action 3>. See `file:line`. <1-2 sentence rationale.>\n\n**Recommendation Index**: <0-based index of the recommended branch in the decision tree above>\n**Recommendation**: <which branch the evidence best supports and why, 1-2 sentences>\n```\n\n## Open Questions\n\nFor each item in the Open Questions section, produce:\n\n```\n### E-<number>: <title from evaluation>\n\n**Confidence**: <High/Medium/Low>\n**Resolution path**: <\"resolve at your desk\" or \"needs a conversation\">\n\n**Decision tree**:\n- If <condition 1>, then <action 1>. See `file:line` (if available). <1-2 sentence rationale.>\n- If <condition 2>, then <action 2>. See `file:line` (if available). <1-2 sentence rationale.>\n\n**Recommendation Index**: <0-based index of the recommended branch in the decision tree above>\n**Recommendation**: <best guess at resolution path and why, or \"Insufficient evidence — escalate to <suggested stakeholder role>\", 1-2 sentences>\n```\n\n## Confirmed Improvements\n\nRender each Confirmed Improvement as a single bullet in a compact list. No headings per item, no decision trees, no rationale paragraphs.\n\n- **E-<number>: <title>** (Confidence: <High/Medium/Low>) — <recommended action, 1 sentence>\n\n## Round Agreement Summary\n\nAfter all items are processed, produce a summary section that groups items by round agreement status from the evaluation:\n\n### Points of Disagreement\nFor items where the evaluation marked `rounds disagree` or `single round only` — including `**Prior Round**: Added` items that failed the two-axis check and landed in Needs Scrutiny — list as bullets with the E-number, the nature of the disagreement, and a 1-sentence explanation of why this disagreement matters for the ticket (e.g., it indicates an architectural ambiguity, a scope question, or a standards gap).\n\nIf no items were marked as disagreements, write: \"All reviewed points had round consensus. No disagreement-driven risks identified.\"\n\n### Points of Agreement\nFor items where the evaluation marked `both rounds agree`: list as bullets with the E-number and a half-sentence noting the shared conclusion. These items required minimal analysis and are lower risk.\n\n### Gaps Captured by Second Round\nFor items where the evaluation marked `gap captured` (sound `**Prior Round**: Added` items that were confirmed as Confirmed Improvements): list as bullets with the E-number and a half-sentence noting the gap the second round surfaced. These items did not require a decision — they are already in Confirmed Improvements — but are surfaced here so the reviewer sees what the second-round analysis added on top of the first round.\n\nIf no gaps were captured, write: \"The second round did not surface any net-new confirmed improvements.\"\n\n## Decision Tree Rules\n\n- Each decision tree must have 2-4 branches. Do not exceed 4.\n- Each branch must end with a concrete, actionable step (not \"investigate further\").\n- Cite relevant code in `file:line` format where possible. If no code reference exists, omit the citation rather than fabricating one.\n- Cap each branch at 2-3 sentences total (including the action and rationale).\n- `**Recommendation Index**` must be the 0-based index of the recommended branch in the decision tree above. The first branch is index 0, the second is index 1, etc.\n- **\"resolve at your desk\"**: The item can be resolved through technical investigation — reading code, running tests, or checking configuration. No stakeholder input needed.\n- **\"needs a conversation\"**: The item involves a product decision, scope question, or cross-team dependency that cannot be resolved from the codebase alone.\n\n## Confidence Tags\n\nAssign confidence based on codebase evidence strength from the evaluation:\n- **High**: The evaluation cites specific `file:line` references that directly support the assessment.\n- **Medium**: The evaluation references related code patterns or architectural conventions, but not the exact code in question.\n- **Low**: No direct codebase evidence. Assessment is based on general reasoning or domain knowledge.\n\n## Edge Cases\n\n- If the evaluation contains zero items in Needs Scrutiny, write: \"No items flagged for scrutiny. All reviewed suggestions were either confirmed or remain open questions.\"\n- If the evaluation contains zero items in Open Questions, write: \"No open questions identified. All ambiguities were resolved through codebase analysis.\"\n- If both Needs Scrutiny and Open Questions are empty, include only the Confirmed Improvements section and add a summary: \"All suggestions from the review were confirmed as grounded improvements. No decision trees are needed.\"\n- If the evaluation does not contain Round Agreement fields (e.g., it was generated before this format), infer agreement status from the Source field: if both rounds are cited with the same conclusion, treat as agreement; if only one round is cited or they conflict, treat as disagreement.\n\n5. Save the resolution guide to `{docs_dir}/review/{ticket_key}-resolution-guide.md`. Output only the resolution guide — no meta-commentary.\n",
524
549
  "update-ticket-rewrite.md": "Rewrite the Jira ticket description for {ticket_key} using the generated clarifying questions and critique documents.\n\n1. Fetch the current ticket description using the `get_ticket` tool with ticket_number `{ticket_key}`.\n2. Read the clarifying questions from the local file saved by the previous step (check `{docs_dir}/clarifying-questions/` for `{ticket_key}-clarifying-questions.md`). For each best-guess answer, verify it against the codebase using file search and code grep. Accept verified answers, correct inaccurate ones with evidence, and let ambiguous ones stand.\n3. Read the critique from the local file saved by the previous step (check `{docs_dir}/ticket-critiques/` for `{ticket_key}-ticket-quality-critique.md`). Address all Requested Changes. Apply Points to Consider selectively — accept genuine improvements, skip stylistic preferences.\n4. Write the rewritten ticket in standard markdown format (not Jira wiki markup). Preserve the Summary, Requirements, and Acceptance Criteria structure.\n5. Save the output to `{docs_dir}/tickets/{ticket_key}.md`. Output only the clean rewritten ticket — no meta-commentary.\n",
525
- "write-epic-summary.md": "Synthesize all sub-task explorations into a final overview document.\n\n## Instructions\n\n1. First, use a terminal command or glob pattern to list all files in `{docs_dir}/epic-plans/{epic_slug}/explorations/`. Then read each file. Do not guess filenames — discover them dynamically.\n\n2. Also read:\n - `{docs_dir}/epic-plans/{epic_slug}/research-findings.md`\n - `{docs_dir}/epic-plans/{epic_slug}/decomposition.md`\n\n3. Synthesize the information into an overview and write it to `{docs_dir}/epic-plans/{epic_slug}/overview.md` with the following required sections:\n\n```markdown\n# Epic Overview: {epic title derived from description}\n\n## Epic Description and Goals\n{Summary of the epic's purpose, scope, and desired outcomes.}\n\n## Research Summary\n{Key external findings that informed the decomposition. If no research was performed, state \"No external research was needed.\"}\n\n## Sub-task List\n{Numbered list of all sub-tasks with relative markdown links to their exploration docs.}\n1. [Sub-task title](explorations/01-subtask-slug.md) — one-line summary\n2. [Sub-task title](explorations/02-subtask-slug.md) — one-line summary\n...\n\n## Dependency Graph\n{Textual list showing execution ordering and dependencies between sub-tasks.}\n- Sub-task 1: No dependencies (start here)\n- Sub-task 2: Depends on Sub-task 1\n- Sub-task 3: Depends on Sub-task 1\n- Sub-task 4: Depends on Sub-tasks 2, 3\n...\n\n## Next Steps\n{One-line summaries for each sub-task, specifically formatted so they can be copy-pasted directly into the `/write-ticket` command. Each line should be a self-contained ticket description.}\n```\n\n4. After writing the overview, display the file path to the user and summarize the epic plan.\n"
550
+ "write-epic-summary.md": "Synthesize all sub-task explorations into a final overview document.\n\n## Instructions\n\n1. First, use a terminal command or glob pattern to list all files in `{docs_dir}/epic-plans/{epic_slug}/explorations/`. Then read each file. Do not guess filenames — discover them dynamically.\n\n2. Also read:\n - `{docs_dir}/epic-plans/{epic_slug}/research-findings.md`\n - `{docs_dir}/epic-plans/{epic_slug}/epic-plan.md`\n\n3. Synthesize the information into an overview and write it to `{docs_dir}/epic-plans/{epic_slug}/overview.md` with the following required sections:\n\n```markdown\n# Epic Overview: {epic title derived from description}\n\n## Epic Description and Goals\n{Summary of the epic's purpose, scope, and desired outcomes.}\n\n## Research Summary\n{Key external findings that informed the decomposition. If no research was performed, state \"No external research was needed.\"}\n\n## Sub-task List\n{Numbered list of all sub-tasks with relative markdown links to their exploration docs.}\n1. [Sub-task title](explorations/01-subtask-slug.md) — one-line summary\n2. [Sub-task title](explorations/02-subtask-slug.md) — one-line summary\n...\n\n## Dependency Graph\n{Textual list showing execution ordering and dependencies between sub-tasks.}\n- Sub-task 1: No dependencies (start here)\n- Sub-task 2: Depends on Sub-task 1\n- Sub-task 3: Depends on Sub-task 1\n- Sub-task 4: Depends on Sub-tasks 2, 3\n...\n\n## Next Steps\n{One-line summaries for each sub-task, specifically formatted so they can be copy-pasted directly into the `/write-ticket` command. Each line should be a self-contained ticket description.}\n```\n\n4. After writing the overview, display the file path to the user and summarize the epic plan.\n"
526
551
  };
@@ -0,0 +1,80 @@
1
+ /**
2
+ * Update check logic — extracted for testability.
3
+ * The main index.ts imports and calls checkForUpdate().
4
+ */
5
+ import { writeFile, mkdir, readFile } from "fs/promises";
6
+ import path from "path";
7
+ import os from "os";
8
+ import { VERSION } from "./version.generated.js";
9
+ const CACHE_TTL = 86400000; // 24 hours
10
+ const FETCH_TIMEOUT = 3000;
11
+ const REGISTRY_URL = "https://registry.npmjs.org/@bridge_gpt/mcp-server/latest";
12
+ export function getCachePath() {
13
+ return path.join(os.homedir(), ".config", "@bridge_gpt", "mcp-server", "update-check.json");
14
+ }
15
+ /**
16
+ * Compare two semver strings. Returns true if `latest` is strictly greater.
17
+ */
18
+ export function isNewerVersion(current, latest) {
19
+ const c = current.split(".").map(Number);
20
+ const l = latest.split(".").map(Number);
21
+ for (let i = 0; i < 3; i++) {
22
+ if ((l[i] ?? 0) > (c[i] ?? 0))
23
+ return true;
24
+ if ((l[i] ?? 0) < (c[i] ?? 0))
25
+ return false;
26
+ }
27
+ return false;
28
+ }
29
+ /**
30
+ * Checks the npm registry for a newer version of @bridge_gpt/mcp-server.
31
+ * Results are cached for 24 hours. Returns null if the check fails.
32
+ */
33
+ export async function checkForUpdate() {
34
+ try {
35
+ const cachePath = getCachePath();
36
+ const cacheDir = path.dirname(cachePath);
37
+ let latestVersion = null;
38
+ // Try reading cache
39
+ try {
40
+ const raw = await readFile(cachePath, "utf-8");
41
+ const cache = JSON.parse(raw);
42
+ if (cache &&
43
+ typeof cache.lastCheck === "number" &&
44
+ typeof cache.latestVersion === "string" &&
45
+ Date.now() - cache.lastCheck < CACHE_TTL) {
46
+ latestVersion = cache.latestVersion;
47
+ }
48
+ }
49
+ catch {
50
+ // Cache missing, corrupt, or stale — will fetch fresh
51
+ }
52
+ // Fetch from registry if no valid cache
53
+ if (!latestVersion) {
54
+ const controller = new AbortController();
55
+ const timeout = setTimeout(() => controller.abort(), FETCH_TIMEOUT);
56
+ try {
57
+ const res = await fetch(REGISTRY_URL, { signal: controller.signal });
58
+ const data = await res.json();
59
+ if (data.version) {
60
+ latestVersion = data.version;
61
+ await mkdir(cacheDir, { recursive: true });
62
+ await writeFile(cachePath, JSON.stringify({ lastCheck: Date.now(), latestVersion }), "utf-8");
63
+ }
64
+ }
65
+ finally {
66
+ clearTimeout(timeout);
67
+ }
68
+ }
69
+ if (!latestVersion)
70
+ return null;
71
+ return {
72
+ updateAvailable: isNewerVersion(VERSION, latestVersion),
73
+ currentVersion: VERSION,
74
+ latestVersion,
75
+ };
76
+ }
77
+ catch {
78
+ return null;
79
+ }
80
+ }
@@ -0,0 +1,2 @@
1
+ // AUTO-GENERATED — do not edit manually. Regenerate with: npm run build
2
+ export const VERSION = "0.1.11";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bridge_gpt/mcp-server",
3
- "version": "0.1.9",
3
+ "version": "0.1.11",
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",
@@ -11,15 +11,17 @@
11
11
  "build/",
12
12
  "!build/pipeline-utils.test.js",
13
13
  "!build/decision-page-template.test.js",
14
+ "!build/update-check.test.js",
15
+ "!build/cli-upgrade.test.js",
14
16
  "pipelines/",
15
17
  "README.md",
16
18
  "LICENSE"
17
19
  ],
18
20
  "scripts": {
19
- "build": "node scripts/bundle-pipelines.js && node scripts/bundle-commands.js && node scripts/bundle-agents.js && tsc",
21
+ "build": "node scripts/bundle-version.js && node scripts/bundle-pipelines.js && node scripts/bundle-commands.js && node scripts/bundle-agents.js && tsc",
20
22
  "postbuild": "node scripts/prepend-shebang.cjs",
21
23
  "start": "node build/index.js",
22
- "test": "node --test build/pipeline-utils.test.js",
24
+ "test": "node --test build/pipeline-utils.test.js build/update-check.test.js build/cli-upgrade.test.js",
23
25
  "prepublishOnly": "npm run build && node scripts/verify-shebang.cjs"
24
26
  },
25
27
  "dependencies": {
@@ -47,9 +47,9 @@
47
47
  "tool": "update_jira_status",
48
48
  "params": {
49
49
  "ticket_number": "{ticket_key}",
50
- "target_status": "In Review"
50
+ "target_status": "auto"
51
51
  },
52
- "description": "Transition ticket to In Review",
52
+ "description": "Resolve and transition ticket to post-PR status",
53
53
  "on_error": "warn_and_continue"
54
54
  },
55
55
  {
@@ -211,6 +211,28 @@
211
211
  },
212
212
  "description": "Upload style_correctness_standards to config",
213
213
  "requires_approval": true
214
+ },
215
+ {
216
+ "type": "mcp_call",
217
+ "tool": "get_config_field",
218
+ "params": { "field_name": "design_principles" },
219
+ "description": "Fetch current design_principles value",
220
+ "on_error": "warn_and_continue"
221
+ },
222
+ {
223
+ "type": "agent_task",
224
+ "instruction_file": "learn-design-principles.md",
225
+ "description": "Learn and document design principles"
226
+ },
227
+ {
228
+ "type": "mcp_call",
229
+ "tool": "update_config_field",
230
+ "params": {
231
+ "field_name": "design_principles",
232
+ "file_path": "{docs_dir}/standards/design_principles.md"
233
+ },
234
+ "description": "Upload design_principles to config",
235
+ "requires_approval": true
214
236
  }
215
237
  ]
216
238
  }