@bridge_gpt/mcp-server 0.2.18 → 0.2.19
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/CONDUCTOR.md +75 -0
- package/README.md +2 -2
- package/build/agent-capabilities/probe-context.js +13 -3
- package/build/agent-capabilities/probes.js +262 -11
- package/build/agent-capabilities/reporter.js +1 -0
- package/build/agents.generated.js +1 -1
- package/build/backend-warnings.js +44 -0
- package/build/claude-settings.js +129 -0
- package/build/commands.generated.js +1 -0
- package/build/conductor/bridge-api-client.js +7 -7
- package/build/conductor/cli.js +65 -12
- package/build/conductor/deny-enforcement-preflight.js +96 -0
- package/build/conductor/doctor.js +183 -2
- package/build/conductor/epic-reconcile.js +9 -1
- package/build/conductor/epic-runtime.js +403 -43
- package/build/conductor/epic-state.js +7 -0
- package/build/conductor/errors.js +115 -3
- package/build/conductor/event-accessors.js +28 -10
- package/build/conductor/merge-ledger.js +6 -4
- package/build/conductor/pr-ci-producer.js +17 -2
- package/build/conductor/producer-ledger.js +1 -1
- package/build/conductor/store.js +161 -18
- package/build/conductor/supervisor-merge.js +32 -5
- package/build/conductor/taxonomy.js +8 -0
- package/build/conductor/tools.js +28 -6
- package/build/conductor/worker-ledger-cli.js +244 -0
- package/build/conductor-bin.js +1884 -6917
- package/build/doctor.js +8 -0
- package/build/executor/cli.js +229 -0
- package/build/executor/credentials.js +65 -0
- package/build/executor/deps.js +117 -0
- package/build/executor/env.js +79 -0
- package/build/executor/heartbeat.js +59 -0
- package/build/executor/http-client.js +131 -0
- package/build/executor/index.js +10 -0
- package/build/executor/job-errors.js +55 -0
- package/build/executor/job-log-registry.js +110 -0
- package/build/executor/job-runner.js +688 -0
- package/build/executor/job-types.js +60 -0
- package/build/executor/merge-job.js +155 -0
- package/build/executor/observation.js +123 -0
- package/build/executor/permissions.js +79 -0
- package/build/executor/preflight.js +144 -0
- package/build/executor/process.js +81 -0
- package/build/executor/prompt-spec.js +235 -0
- package/build/executor/results.js +134 -0
- package/build/executor/resume-pre-spawn.js +179 -0
- package/build/executor/runner.js +98 -0
- package/build/executor/terminal-mutation.js +34 -0
- package/build/executor/test-clock.js +109 -0
- package/build/executor/types.js +18 -0
- package/build/executor/verdict-artifact.js +53 -0
- package/build/executor/viewer-tabs.js +78 -0
- package/build/executor/watch-cli.js +113 -0
- package/build/executor/worker-command.js +106 -0
- package/build/executor/worker-finalization.js +97 -0
- package/build/executor/worker-log.js +92 -0
- package/build/executor/worktree-gc.js +134 -0
- package/build/executor/worktree-inspection.js +86 -0
- package/build/executor/worktree.js +103 -0
- package/build/index.js +11222 -8544
- package/build/mcp-invoke.js +19 -3
- package/build/mcp-provisioning.js +31 -25
- package/build/mcp-registration-doctor.js +27 -7
- package/build/mcp-server-invocation.js +152 -0
- package/build/pipelines.generated.js +1 -1
- package/build/readme.generated.js +1 -1
- package/build/sfcc/reads-site-preference.js +52 -19
- package/build/start-tickets-conductor.js +25 -93
- package/build/start-tickets-prereqs.js +152 -1
- package/build/start-tickets.js +96 -158
- package/build/version.generated.js +1 -1
- package/build/visual-diff-worker.js +313 -0
- package/build/visual-diff.js +632 -0
- package/build/worktree-core.js +202 -0
- package/package.json +8 -4
- package/public/css/main.min.css +39 -0
- package/public/css/main.min.css.map +1 -1
- package/public/js/main.min.js +7924 -1
- package/public/js/main.min.js.map +1 -1
- package/smoke-test/SMOKE-TEST.md +2 -1
package/build/mcp-invoke.js
CHANGED
|
@@ -32,10 +32,15 @@ import { getThirdPartyTargetDefinition, resolveThirdPartyTargetEnv, validateThir
|
|
|
32
32
|
export function getMcpInvokeUsage() {
|
|
33
33
|
return [
|
|
34
34
|
"Usage:",
|
|
35
|
-
"
|
|
35
|
+
" node <abs>/mcp_server/build/index.js mcp-invoke --target <target> --project-root <ABS_WORKTREE_PATH>",
|
|
36
|
+
"",
|
|
37
|
+
" (npm-channel fallback may invoke the same shim through a package spec, e.g.",
|
|
38
|
+
" npx -y @bridge_gpt/mcp-server@latest mcp-invoke --target <target> --project-root <ABS_WORKTREE_PATH>)",
|
|
36
39
|
"",
|
|
37
40
|
"Internal worktree shim: resolves the target's launch command and credentials",
|
|
38
41
|
"from the given project root, then spawns the real MCP server over stdio.",
|
|
42
|
+
"Argument parsing is identical regardless of how this process was launched",
|
|
43
|
+
"(absolute-path node invocation vs npm-channel npx).",
|
|
39
44
|
"",
|
|
40
45
|
"Flags:",
|
|
41
46
|
" --target <target> MCP target to launch. 'bapi' launches the Bridge",
|
|
@@ -264,11 +269,22 @@ export async function resolveBapiInvocation(projectRoot, deps) {
|
|
|
264
269
|
if (!credResult.ok) {
|
|
265
270
|
return { ok: false, error: `failed to resolve credentials: ${credResult.error}` };
|
|
266
271
|
}
|
|
267
|
-
const
|
|
272
|
+
const overlay = {
|
|
268
273
|
BAPI_API_KEY: credResult.credentials.apiKey,
|
|
269
274
|
BAPI_PROJECT_ROOT: projectRoot,
|
|
270
275
|
BAPI_REPO_NAME: repoName,
|
|
271
|
-
}
|
|
276
|
+
};
|
|
277
|
+
// BAPI-527: `CONDUCTOR_NODE_PATH` is NON-SECRET operational metadata (the
|
|
278
|
+
// captured conductor Node path used by the worker-ledger CLI subprocess).
|
|
279
|
+
// `buildChildEnv` already inherits the full parent env, so this re-affirms the
|
|
280
|
+
// pass-through explicitly and ONLY when present — it is a non-secret allowlist
|
|
281
|
+
// entry, never a credential. It is emphatically NOT the MCP server's re-exec
|
|
282
|
+
// command: the spawned command stays `deps.execPath` + `[scriptPath]` below.
|
|
283
|
+
const conductorNodePath = deps.env.CONDUCTOR_NODE_PATH;
|
|
284
|
+
if (typeof conductorNodePath === "string" && conductorNodePath.trim().length > 0) {
|
|
285
|
+
overlay.CONDUCTOR_NODE_PATH = conductorNodePath;
|
|
286
|
+
}
|
|
287
|
+
const env = buildChildEnv(deps.env, overlay);
|
|
272
288
|
return {
|
|
273
289
|
ok: true,
|
|
274
290
|
invocation: { command: deps.execPath, args: [deps.scriptPath], env },
|
|
@@ -18,9 +18,9 @@
|
|
|
18
18
|
* to avoid a runtime import cycle.
|
|
19
19
|
*/
|
|
20
20
|
import path from "path";
|
|
21
|
-
import { VERSION } from "./version.generated.js";
|
|
22
21
|
import { readBridgeConfig } from "./bridge-config.js";
|
|
23
22
|
import { getThirdPartyTargetDefinition, validateThirdPartyTargetManifestEntry, } from "./third-party-mcp-targets.js";
|
|
23
|
+
import { buildMcpShimCommand, } from "./mcp-server-invocation.js";
|
|
24
24
|
// ---------------------------------------------------------------------------
|
|
25
25
|
// Pure helpers
|
|
26
26
|
// ---------------------------------------------------------------------------
|
|
@@ -55,27 +55,27 @@ export function serverNameForMcpTarget(target) {
|
|
|
55
55
|
return target === "bapi" ? "bridge-api" : target;
|
|
56
56
|
}
|
|
57
57
|
/**
|
|
58
|
-
* Build a secret-free shim entry for any target
|
|
59
|
-
* carry the target name plus the absolute project root; there is
|
|
60
|
-
* no `env` block.
|
|
58
|
+
* Build a secret-free shim entry for any target from a structured invocation.
|
|
59
|
+
* The command/args carry the target name plus the absolute project root; there is
|
|
60
|
+
* intentionally no `env` block. The absolute-build-path form serializes as
|
|
61
|
+
* `{ command: "node", args: ["<abs>/build/index.js", "mcp-invoke", …] }` (the
|
|
62
|
+
* node executable is a single substitutable field), and the npm-channel fallback
|
|
63
|
+
* as `{ command: "npx", args: ["-y", "<spec>", "mcp-invoke", …] }`.
|
|
64
|
+
*
|
|
65
|
+
* BAPI-527: `.mcp.json` remains intentionally ENV-FREE. `CONDUCTOR_NODE_PATH`
|
|
66
|
+
* (the captured conductor Node path for the worker-ledger CLI subprocess) is NOT
|
|
67
|
+
* written here — it is inherited at runtime from the worker SHELL env that
|
|
68
|
+
* `start-tickets` injects at the spawn boundary, never stored as a registration
|
|
69
|
+
* value. Keeping the shim env-free preserves the credential-safety invariant
|
|
70
|
+
* (secrets are resolved by the `mcp-invoke` shim at launch, not persisted in the
|
|
71
|
+
* worktree) and equally avoids persisting non-secret operational metadata here.
|
|
61
72
|
*/
|
|
62
|
-
export function buildShimMcpServerEntry(target, absoluteWorktreePath) {
|
|
63
|
-
return
|
|
64
|
-
command: "npx",
|
|
65
|
-
args: [
|
|
66
|
-
"-y",
|
|
67
|
-
`@bridge_gpt/mcp-server@${VERSION}`,
|
|
68
|
-
"mcp-invoke",
|
|
69
|
-
"--target",
|
|
70
|
-
target,
|
|
71
|
-
"--project-root",
|
|
72
|
-
absoluteWorktreePath,
|
|
73
|
-
],
|
|
74
|
-
};
|
|
73
|
+
export function buildShimMcpServerEntry(target, absoluteWorktreePath, invocation) {
|
|
74
|
+
return buildMcpShimCommand(invocation, target, absoluteWorktreePath);
|
|
75
75
|
}
|
|
76
76
|
/** Back-compat wrapper around `buildShimMcpServerEntry("bapi", ...)`. */
|
|
77
|
-
export function buildBridgeApiShimMcpServerEntry(absoluteWorktreePath) {
|
|
78
|
-
return buildShimMcpServerEntry("bapi", absoluteWorktreePath);
|
|
77
|
+
export function buildBridgeApiShimMcpServerEntry(absoluteWorktreePath, invocation) {
|
|
78
|
+
return buildShimMcpServerEntry("bapi", absoluteWorktreePath, invocation);
|
|
79
79
|
}
|
|
80
80
|
/**
|
|
81
81
|
* Convert all supported manifest `mcp` entries into a map of server name -> shim
|
|
@@ -84,12 +84,12 @@ export function buildBridgeApiShimMcpServerEntry(absoluteWorktreePath) {
|
|
|
84
84
|
* or incomplete non-`bapi` targets are skipped with a secret-free warning (they
|
|
85
85
|
* never appear in the returned entries).
|
|
86
86
|
*/
|
|
87
|
-
export function buildMcpServerEntriesForManifest(manifest, absoluteWorktreePath) {
|
|
87
|
+
export function buildMcpServerEntriesForManifest(manifest, absoluteWorktreePath, invocation) {
|
|
88
88
|
const entries = {};
|
|
89
89
|
const warnings = [];
|
|
90
90
|
for (const mcp of manifest.mcp) {
|
|
91
91
|
if (mcp.target === "bapi") {
|
|
92
|
-
entries[serverNameForMcpTarget("bapi")] = buildShimMcpServerEntry("bapi", absoluteWorktreePath);
|
|
92
|
+
entries[serverNameForMcpTarget("bapi")] = buildShimMcpServerEntry("bapi", absoluteWorktreePath, invocation);
|
|
93
93
|
continue;
|
|
94
94
|
}
|
|
95
95
|
const definition = getThirdPartyTargetDefinition(mcp.target);
|
|
@@ -102,9 +102,9 @@ export function buildMcpServerEntriesForManifest(manifest, absoluteWorktreePath)
|
|
|
102
102
|
warnings.push(`MCP target '${mcp.target}' registration skipped: ${validation.error}.`);
|
|
103
103
|
continue;
|
|
104
104
|
}
|
|
105
|
-
entries[serverNameForMcpTarget(mcp.target)] = buildShimMcpServerEntry(mcp.target, absoluteWorktreePath);
|
|
105
|
+
entries[serverNameForMcpTarget(mcp.target)] = buildShimMcpServerEntry(mcp.target, absoluteWorktreePath, invocation);
|
|
106
106
|
}
|
|
107
|
-
return { entries, warnings };
|
|
107
|
+
return { entries, warnings, registrationForm: invocation.form };
|
|
108
108
|
}
|
|
109
109
|
/** Both registration files written for every provisioned worktree. */
|
|
110
110
|
export function getWorktreeMcpRegistrationTargets(worktreePath, platform) {
|
|
@@ -305,7 +305,7 @@ export async function provisionMcpRegistrationForWorktree(row, deps) {
|
|
|
305
305
|
if (!normalized.ok) {
|
|
306
306
|
return { ...row, status: "spawn-failed", error: `MCP provisioning failed: ${normalized.error}` };
|
|
307
307
|
}
|
|
308
|
-
const built = buildMcpServerEntriesForManifest(read.manifest, normalized.path);
|
|
308
|
+
const built = buildMcpServerEntriesForManifest(read.manifest, normalized.path, deps.mcpServerInvocation);
|
|
309
309
|
if (Object.keys(built.entries).length === 0) {
|
|
310
310
|
// Nothing supported to write (e.g. a manifest with no bapi target and no
|
|
311
311
|
// supported Tier-2 targets). Surface any warnings but leave status unchanged.
|
|
@@ -318,10 +318,16 @@ export async function provisionMcpRegistrationForWorktree(row, deps) {
|
|
|
318
318
|
return { ...row, status: "spawn-failed", error: `MCP provisioning failed: ${result.error}` };
|
|
319
319
|
}
|
|
320
320
|
}
|
|
321
|
+
// Record the resolved registration form for audit (path-free — the form label
|
|
322
|
+
// only, never the absolute server entry path, which could leak a username).
|
|
323
|
+
let result = { ...row, mcpRegistrationForm: built.registrationForm };
|
|
324
|
+
result = withWarnings(result, built.warnings);
|
|
325
|
+
if (built.registrationForm !== "absolute-build-path") {
|
|
326
|
+
result = withWarning(result, "MCP registration used npm-channel fallback because an on-disk build entry was not resolvable.");
|
|
327
|
+
}
|
|
321
328
|
// Pre-approve the just-registered servers so Claude Code does not prompt to
|
|
322
329
|
// trust them in this fresh worktree path. Best-effort: a failure here only
|
|
323
330
|
// loses the convenience of skipping the prompt, so warn and still spawn.
|
|
324
|
-
let result = withWarnings(row, built.warnings);
|
|
325
331
|
const serverNames = Object.keys(built.entries);
|
|
326
332
|
const trust = await writeClaudeServerTrustSettings(normalized.path, serverNames, deps);
|
|
327
333
|
if (!trust.ok) {
|
|
@@ -35,22 +35,42 @@ function flagValue(args, flag) {
|
|
|
35
35
|
return undefined;
|
|
36
36
|
}
|
|
37
37
|
/**
|
|
38
|
-
* True when `entry` is a secret-free Bridge API shim registration
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
38
|
+
* True when `entry` is a secret-free Bridge API shim registration pointing at
|
|
39
|
+
* THIS worktree. Two forms are accepted (BAPI-526):
|
|
40
|
+
*
|
|
41
|
+
* - Absolute-build-path (PRIMARY): an absolute `node` (or absolute Node-path)
|
|
42
|
+
* invocation of the conductor's own compiled server, e.g.
|
|
43
|
+
* `{ command: "node", args: ["<abs>/mcp_server/build/index.js", "mcp-invoke",
|
|
44
|
+
* "--target", "bapi", "--project-root", "<worktreeRoot>"] }`. `command` may be
|
|
45
|
+
* any non-empty string ("node" or a future absolute Node path); `args[0]` is a
|
|
46
|
+
* non-empty server entry path.
|
|
47
|
+
* - npm-channel (LEGACY/FALLBACK): `{ command: "npx", args: ["-y",
|
|
48
|
+
* "@bridge_gpt/mcp-server@<spec>", "mcp-invoke", "--target", "bapi",
|
|
49
|
+
* "--project-root", "<worktreeRoot>"] }`.
|
|
50
|
+
*
|
|
51
|
+
* In both forms `--target` must be `bapi` and `--project-root` must match the
|
|
52
|
+
* given worktree exactly. The server entry path is NOT required to sit under any
|
|
53
|
+
* particular username/path prefix.
|
|
42
54
|
*/
|
|
43
55
|
export function isBridgeApiShimEntry(entry, worktreeRoot) {
|
|
44
56
|
if (!entry || typeof entry !== "object" || Array.isArray(entry))
|
|
45
57
|
return false;
|
|
46
58
|
const candidate = entry;
|
|
47
|
-
if (candidate.command !== "
|
|
59
|
+
if (typeof candidate.command !== "string" || candidate.command.length === 0)
|
|
48
60
|
return false;
|
|
49
61
|
if (!Array.isArray(candidate.args))
|
|
50
62
|
return false;
|
|
51
63
|
const args = candidate.args.filter((a) => typeof a === "string");
|
|
52
|
-
if (
|
|
53
|
-
|
|
64
|
+
if (candidate.command === "npx") {
|
|
65
|
+
// npm-channel fallback: must reference the package spec.
|
|
66
|
+
if (!args.some((a) => a.startsWith("@bridge_gpt/mcp-server")))
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
// Absolute-build-path: first arg must be a non-empty server entry path.
|
|
71
|
+
if (typeof args[0] !== "string" || args[0].length === 0)
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
54
74
|
if (!args.includes("mcp-invoke"))
|
|
55
75
|
return false;
|
|
56
76
|
if (flagValue(args, "--target") !== "bapi")
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared, dependency-injected model for how a dispatched worker launches the
|
|
3
|
+
* Bridge MCP `mcp-invoke` shim (BAPI-526, Change A).
|
|
4
|
+
*
|
|
5
|
+
* The conductor used to register each dispatched worktree's `.mcp.json` against
|
|
6
|
+
* an EXACT npm version (`npx -y @bridge_gpt/mcp-server@<VERSION> mcp-invoke …`).
|
|
7
|
+
* When that version was built but not yet published, `npx` returned ETARGET/404
|
|
8
|
+
* and the worker had NO Bridge MCP tools. This module lets provisioning instead
|
|
9
|
+
* invoke the conductor's own on-disk build by ABSOLUTE PATH
|
|
10
|
+
* (`node <abs>/mcp_server/build/index.js mcp-invoke …`) — removing the
|
|
11
|
+
* npm-registry publish dependency and guaranteeing the worker runs the exact MCP
|
|
12
|
+
* code the conductor is running. A resolvable npm-channel spec remains the
|
|
13
|
+
* fallback when an on-disk build entry cannot be resolved (published-package
|
|
14
|
+
* install).
|
|
15
|
+
*
|
|
16
|
+
* SCOPE BOUNDARY (BAPI-526 is detection/registration only): this selects WHICH
|
|
17
|
+
* server file is invoked (`node <absPath>/index.js` vs `npx @…`). It does NOT
|
|
18
|
+
* change WHICH Node binary runs that file — the `nodeExecutable` field is kept as
|
|
19
|
+
* a single substitutable value (never string-concatenated into the command) so
|
|
20
|
+
* the later Ticket 3 (P2) work can point it at `CONDUCTOR_NODE_PATH` without
|
|
21
|
+
* re-plumbing argv construction.
|
|
22
|
+
*/
|
|
23
|
+
import path from "node:path";
|
|
24
|
+
export const MCP_SERVER_PACKAGE_NAME = "@bridge_gpt/mcp-server";
|
|
25
|
+
/**
|
|
26
|
+
* Resolvable npm-channel fallback spec. Deliberately a moving channel (`@latest`)
|
|
27
|
+
* rather than an exact generated version pin, so the fallback path can never
|
|
28
|
+
* reintroduce the ETARGET/404 failure the absolute-path form exists to remove.
|
|
29
|
+
*/
|
|
30
|
+
export const DEFAULT_MCP_SERVER_NPM_CHANNEL_SPEC = "@bridge_gpt/mcp-server@latest";
|
|
31
|
+
/**
|
|
32
|
+
* Build the concrete `{ command, args }` launch pair for a target + worktree from
|
|
33
|
+
* a structured invocation. Pure — no filesystem or process access. The
|
|
34
|
+
* `nodeExecutable` is returned as the standalone `command`, never concatenated
|
|
35
|
+
* with the server entry path, so `CONDUCTOR_NODE_PATH` can substitute it later.
|
|
36
|
+
*/
|
|
37
|
+
export function buildMcpShimCommand(invocation, target, absoluteWorktreePath) {
|
|
38
|
+
if (invocation.form === "absolute-build-path") {
|
|
39
|
+
return {
|
|
40
|
+
command: invocation.nodeExecutable,
|
|
41
|
+
args: [
|
|
42
|
+
invocation.serverEntryPath,
|
|
43
|
+
"mcp-invoke",
|
|
44
|
+
"--target",
|
|
45
|
+
target,
|
|
46
|
+
"--project-root",
|
|
47
|
+
absoluteWorktreePath,
|
|
48
|
+
],
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
return {
|
|
52
|
+
command: "npx",
|
|
53
|
+
args: [
|
|
54
|
+
"-y",
|
|
55
|
+
invocation.packageSpec,
|
|
56
|
+
"mcp-invoke",
|
|
57
|
+
"--target",
|
|
58
|
+
target,
|
|
59
|
+
"--project-root",
|
|
60
|
+
absoluteWorktreePath,
|
|
61
|
+
],
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Secret-free, path-free audit label for summaries and diagnostics. Returns ONLY
|
|
66
|
+
* the registration form; never the server entry path, username, argv, or package
|
|
67
|
+
* cache path (any of which can leak a username through a filesystem path).
|
|
68
|
+
*/
|
|
69
|
+
export function formatMcpShimInvocationForAudit(invocation) {
|
|
70
|
+
return invocation.form;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Resolve the package root from a module URL by walking the module file path and
|
|
74
|
+
* locating the nearest `src` or `build` path segment; the package root is the
|
|
75
|
+
* portion BEFORE that segment. Returns forward-slash form so it is deterministic
|
|
76
|
+
* across platforms (Node accepts forward slashes on Windows). Does NOT consult
|
|
77
|
+
* `process.cwd()`. Returns `null` when neither `src` nor `build` appears.
|
|
78
|
+
*/
|
|
79
|
+
export function resolvePackageRootFromModuleUrl(moduleUrl) {
|
|
80
|
+
// Parse the URL's pathname directly rather than via fileURLToPath: on Windows,
|
|
81
|
+
// fileURLToPath throws ERR_INVALID_FILE_URL_PATH for a drive-less `file:///…`
|
|
82
|
+
// URL, whereas the pathname (`/repo/mcp_server/…`) is available on every
|
|
83
|
+
// platform. This keeps resolution deterministic across POSIX and Windows.
|
|
84
|
+
let pathname;
|
|
85
|
+
try {
|
|
86
|
+
pathname = decodeURIComponent(new URL(moduleUrl).pathname);
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
// A real Windows `import.meta.url` is `file:///C:/…`, whose pathname is
|
|
92
|
+
// `/C:/…`; strip the leading slash so the drive letter leads (Node accepts
|
|
93
|
+
// forward slashes on Windows). Drive-less POSIX pathnames are left unchanged.
|
|
94
|
+
if (/^\/[A-Za-z]:/.test(pathname)) {
|
|
95
|
+
pathname = pathname.slice(1);
|
|
96
|
+
}
|
|
97
|
+
// Split on both separators so a Windows-materialized path (backslashes) and a
|
|
98
|
+
// posix path both segment identically.
|
|
99
|
+
const segments = pathname.split(/[\\/]/);
|
|
100
|
+
let markerIndex = -1;
|
|
101
|
+
for (let i = segments.length - 1; i >= 0; i--) {
|
|
102
|
+
if (segments[i] === "src" || segments[i] === "build") {
|
|
103
|
+
markerIndex = i;
|
|
104
|
+
break;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
if (markerIndex <= 0)
|
|
108
|
+
return null;
|
|
109
|
+
return segments.slice(0, markerIndex).join("/");
|
|
110
|
+
}
|
|
111
|
+
/** Manual basename that tolerates either path separator. */
|
|
112
|
+
function basenameAnySep(filePath) {
|
|
113
|
+
const segments = filePath.split(/[\\/]/);
|
|
114
|
+
return segments[segments.length - 1] ?? "";
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Resolve how the worker MCP shim should be launched, preferring the conductor's
|
|
118
|
+
* own on-disk build by absolute path and falling back to a resolvable npm channel.
|
|
119
|
+
*
|
|
120
|
+
* Resolution order (never uses `process.cwd()`):
|
|
121
|
+
* 1. `<packageRoot>/build/index.js` derived from `deps.moduleUrl`.
|
|
122
|
+
* 2. `deps.argv1` when it is absolute, exists, and its basename is `index.js`.
|
|
123
|
+
* 3. npm-channel fallback (`deps.npmPackageSpec ?? DEFAULT_MCP_SERVER_NPM_CHANNEL_SPEC`).
|
|
124
|
+
*
|
|
125
|
+
* NOTE (scope boundary): this ticket changes WHICH server file is invoked, not
|
|
126
|
+
* WHICH Node runtime runs it. `nodeExecutable` defaults to `"node"` (NOT
|
|
127
|
+
* `process.execPath`) to preserve today's worker runtime selection; Ticket 3 (P2)
|
|
128
|
+
* can later inject `CONDUCTOR_NODE_PATH` into this single field.
|
|
129
|
+
*/
|
|
130
|
+
export function resolveMcpShimInvocationForRuntime(deps) {
|
|
131
|
+
const nodeExecutable = deps.nodeExecutable ?? "node";
|
|
132
|
+
const packageRoot = resolvePackageRootFromModuleUrl(deps.moduleUrl);
|
|
133
|
+
if (packageRoot) {
|
|
134
|
+
const candidate = `${packageRoot}/build/index.js`;
|
|
135
|
+
if (deps.fileExists(candidate)) {
|
|
136
|
+
return { form: "absolute-build-path", nodeExecutable, serverEntryPath: candidate };
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
const argv1 = deps.argv1;
|
|
140
|
+
if (typeof argv1 === "string" &&
|
|
141
|
+
argv1.length > 0 &&
|
|
142
|
+
path.isAbsolute(argv1) &&
|
|
143
|
+
basenameAnySep(argv1) === "index.js" &&
|
|
144
|
+
deps.fileExists(argv1)) {
|
|
145
|
+
return { form: "absolute-build-path", nodeExecutable, serverEntryPath: argv1 };
|
|
146
|
+
}
|
|
147
|
+
return {
|
|
148
|
+
form: "npm-channel",
|
|
149
|
+
command: "npx",
|
|
150
|
+
packageSpec: deps.npmPackageSpec ?? DEFAULT_MCP_SERVER_NPM_CHANNEL_SPEC,
|
|
151
|
+
};
|
|
152
|
+
}
|
|
@@ -702,7 +702,7 @@ export const INSTRUCTIONS = {
|
|
|
702
702
|
"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. Search by filename pattern, search file contents by text pattern, and read relevant files to find:\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\n## Return\n\nConfirm the codebase exploration was written to `{docs_dir}/epic-plans/{epic_slug}/codebase-exploration.md` and return a concise summary of the discovered codebase areas, naming the key files and patterns relevant to the epic.\n",
|
|
703
703
|
"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\n## Return\n\nConfirm one exploration document was written per sub-task under `{docs_dir}/epic-plans/{epic_slug}/explorations/` and return a concise summary of the discovered code areas and recommended approaches across the sub-tasks.\n",
|
|
704
704
|
"frame-goals-and-nfrs.md": "Frame the business goals, desired end-state, and non-functional requirements (NFRs) for this work before any functional decomposition or drafting. When the goals and the desired end-state of the system are clear, the functional requirements become much easier to design accurately. This step is documentary: it records the framing and classifies what is unclear. It does NOT pause and does NOT generate a decision page (interactive surfaces handle that separately).\n\n## Inputs\n\n- The idea or epic description for this run, plus any prior planning artifacts the earlier steps wrote into this run's working directory under `{docs_dir}` (for example: research findings, codebase exploration, resolved uncertainties, duplicate assessment). Read whichever of these exist; proceed without the ones that do not.\n\n## Instructions\n\n1. From the inputs, derive and state plainly:\n - **Business goal** — the business value this work delivers and why it matters.\n - **Desired end-state** — the concrete state the system should reach once this work is done.\n - **System behavior** — how the system must behave to complete its task (the quality attributes in prose, not a feature list).\n\n2. Identify the non-functional requirements. Consider every one of these canonical NFR categories and include the ones that genuinely apply (omit categories that do not):\n - security/privacy\n - performance/latency\n - reliability/failure-modes\n - observability/auditability\n - accessibility/UX\n - data-integrity/migration\n - compatibility\n - operability/config\n - compliance/SOC2\n - rollout/reversibility\n\n For each NFR you include, write three things: the `requirement`, its `implication` (what this requirement changes about the implementation), and a `status`. **An NFR with no concrete implication is boilerplate — drop it rather than record it.**\n\n3. Classify each NFR's `status` with this rubric:\n - `confirmed` — only if it is explicitly stated in the idea/description/standards or is directly observable in the codebase.\n - `assumed` — only if it is a low-risk, conventional, and reversible default.\n - `open` — if it touches architecture, the data model, security, user-visible behavior, migration, or irreversible Jira creation and is not settled. Be willing to mark things `open`: surfacing an unclear NFR is the point of this step.\n\n4. If this work is an epic (it will be decomposed into multiple sub-tasks or child tickets), draft a provisional **recommended implementation order**. For each slice, record a short title, its hard prerequisites (`depends_on` — what must land first), any soft sequencing preferences (`recommended_after` — not hard blockers), and a one-line rationale. Keep hard prerequisites separate from soft sequencing. Do not create Jira dependency links — the order is delivered into the epic downstream.\n\n5. Write the framing to a file named `goals-and-nfrs.md` in this run's working directory — the **same directory the earlier exploration/research steps in this pipeline wrote to** under `{docs_dir}`. Getting this path right matters: downstream steps read `goals-and-nfrs.md` from that exact directory and silently degrade (they see no framing) if it lands elsewhere. The directory differs by pipeline:\n - **plan-epic**: the epic plan directory, `docs/epic-plans/<epic-slug>/` (alongside `codebase-exploration.md` and `epic-plan.md`).\n - **idea-to-ticket**: the run directory, `docs/idea-to-ticket/<slug>-<run-id>/` (alongside `research-pack.md` and `resolved-uncertainties.md`).\n\n Use this structure (no markdown tables, no `- [ ]` checkboxes — BAPI-320 hygiene):\n\n```markdown\n# Goals & Non-Functional Requirements\n\n## Business Goal\n{business goal}\n\n## Desired End-State\n{desired end-state}\n\n## System Behavior\n{how the system must behave to complete its task}\n\n## Non-Functional Requirements\n- **{nfr category}** ({confirmed, assumed, or open}): {the requirement}. Implication: {what it changes about the implementation}.\n- ...\n\n## Recommended Implementation Order\n(Epics only; omit this section for a single task or spike.)\n1. {slice title} — depends on: {hard prerequisites or \"none\"}; recommended after: {soft preferences or \"none\"}. Rationale: {one line}.\n2. ...\n```\n\n## Return\n\nConfirm `goals-and-nfrs.md` was written, report the counts of `confirmed` / `assumed` / `open` NFRs, and state whether a recommended implementation order was produced (epics) or skipped (single task/spike).\n",
|
|
705
|
-
"gather-and-attach-materials.md": "Post-create materials-completeness step. Gather the reachable local text materials a freshly-created ticket references and attach them via `attachment` (operation: `\"upload\"`), while recording everything that is record-only. This is the POST-CREATE half of the upload-time materials-completeness pass (BAPI-423); the PRE-CREATE half — inventorying and writing the `## Materials & Access` section into the draft — already ran in the `jira-ticket-writer` agent.\n\n## Inputs\n\n- `{ticket_number}` — the real Jira key of the already-created ticket (e.g. `BAPI-423`). Attachment is a POST-CREATE step; never attempt to attach before the key exists.\n- `{draft_file_path}` — path to the draft markdown that carries the trailing `## Materials & Access` section.\n- `{auto_approve_external}` — the unattended-vs-interactive signal (named for consistency with `upload-and-track.md`). **Polarity is counter-intuitive: `\"true\"` means UNATTENDED, which is the MORE restrictive mode here** — skip all prompts AND keep external/auth-gated materials record-only (never auto-attach them). It does NOT grant permission to attach external materials. Any other value (including `\"false\"`, missing, or empty) means an interactive invocation that MAY prompt for external/auth-gated materials. Invocations from `write-ticket` and `full-automation` are always unattended (`\"true\"`) for this step.\n\n## Instructions\n\n> **Orchestrator-directed step.** This agent task is authorized to call `attachment` (operations: `list`, `upload`) and `update_ticket_description` as directed below.\n\n1. **Read the record.** Read `{draft_file_path}` and parse its trailing `## Materials & Access` section. Collect the inventoried items grouped under *Reachable Local Files*, *External/Auth-Gated Links*, and *Binary/Image Materials (Record-Only)*. If there is no `## Materials & Access` section, there is nothing to gather — return a no-op success.\n\n2. **Deduplicate first.** Call the `attachment` MCP tool with `operation` set to `\"list\"` and `ticket_number` set to `{ticket_number}` BEFORE uploading anything, so a resumed or re-run invocation does not re-attach a material that is already present. Compare against the deterministic filenames computed in step 4 and skip any that already exist.\n\n3. **Source classification (scheme-based, no network probe).** Honor the classification already recorded in the draft:\n - **Reachable Local Files** (local filesystem paths) are the only **low-risk** materials eligible for auto-attach — proceed to step 4.\n - **External/Auth-Gated Links** (every `http(s)` URI, even if explicitly linked) are **record-only** on unattended paths. If `{auto_approve_external}` is `\"true\"` (or the invocation is from `write-ticket` / `full-automation`), leave them record-only and never auto-attach. (Mind the polarity: `auto_approve_external = \"true\"` means we are in unattended mode, so external materials must stay record-only — `\"true\"` is NOT permission to attach them.) Only an explicitly interactive invocation (`auto_approve_external` is any non-`\"true\"` value) may prompt the user to confirm before attaching.\n - **Binary/Image Materials** are **record-only** in this step: the `attachment` tool's `upload` operation is UTF-8 text only, so never attempt a binary upload. (Binary-upload capability is the fast-follow sibling ticket BAPI-424.)\n\n4. **Gather and size-tier each reachable local text material.** For each low-risk local text material:\n - Read the local file from disk.\n - If the content exceeds **200,000 characters**, SKIP the upload and RECORD it (note the path and that it was skipped for size) — do not attach it.\n - If the content is **<= 200,000 characters**, upload it RAW via `attachment` (operation: `\"upload\"`). Do NOT summarize locally: the backend already summarizes attached text at plan time, so the size tiers are backend behavior this step defers to. The agent performs NO local summarization.\n - Use a deterministic, sanitized filename of the form `{ticket_number}-material-{hash}.md` (using the `{ticket_number}` input from the Inputs section), where `{hash}` is the first 8 hex characters of the SHA-256 digest of the sanitized absolute source path. Pin this algorithm exactly (SHA-256, first 8 hex chars, of the sanitized absolute path) — do NOT substitute another hash — so the same source always maps to the same filename and the dedup in step 2 works across separate sessions and re-runs. Keep the sanitized source provenance inside the attachment body, not only in the filename.\n\n5. **`attachment` upload parameter discipline (Zod).** When calling `attachment` with `operation: \"upload\"`, pass `ticket_number`, the attachment filename, and the text content. OMIT the optional parameters `link_type` and `replace_existing` entirely when they are unused — do NOT pass `null` or empty strings for them. The Zod schemas reject `null`/empty values, so an unused optional parameter must be omitted rather than nulled.\n\n6. **Redact secrets everywhere.** Before writing any URL or access note ANYWHERE — the Jira `## Materials & Access` record, any warning or final-report output, and any local intermediate file — sanitize and redact embedded credentials, SAS tokens, API keys, and basic-auth secrets using a high-visibility placeholder such as `[REDACTED_TOKEN]`. Mirror the backend `_redact_forge_fields()` / `_sanitize_jira_error_message()` patterns. A location/access note must never expose a plaintext secret.\n\n7. **Warn, never halt (error handling).** This step must NEVER halt, prompt-to-fail, or fail the overarching command because a material could not be gathered or attached. Follow the warn-not-halt convention:\n - If an `attachment` upload call fails (or a file disappeared between inventory and upload), warn gracefully and continue with the next material.\n - On such a post-create attach failure, call `update_ticket_description` to record the failure in the issue's `## Materials & Access` record (the material became unavailable only after the issue existed). `update_ticket_description` is an existing MCP tool, not a backend change.\n - Everything knowable PRE-CREATE was already written into the description at create time, so `update_ticket_description` is reserved for these rarer post-create attach failures. This complements the existing `partial_success` recording convention in `upload-and-track.md`.\n - Apply the step 6 redaction to every warning and recorded note.\n\n## Return\n\nConfirm the outcome: which local materials were attached (with their deterministic filenames), which were skipped/recorded (over-size, external/auth-gated, or binary/image), any attach failures recorded via `update_ticket_description`, and that no failure halted the run.\n",
|
|
705
|
+
"gather-and-attach-materials.md": "Post-create materials-completeness step. Gather the reachable local text materials a freshly-created ticket references and attach them via `attachment` (operation: `\"upload\"`), while recording everything that is record-only. This is the POST-CREATE half of the upload-time materials-completeness pass (BAPI-423); the PRE-CREATE half — inventorying and writing the `## Materials & Access` section into the draft — already ran in the `jira-ticket-writer` agent.\n\n## Inputs\n\n- `{ticket_number}` — the real Jira key of the already-created ticket (e.g. `BAPI-423`). Attachment is a POST-CREATE step; never attempt to attach before the key exists.\n- `{draft_file_path}` — path to the draft markdown that carries the trailing `## Materials & Access` section.\n- `{auto_approve_external}` — the unattended-vs-interactive signal (named for consistency with `upload-and-track.md`). **Polarity is counter-intuitive: `\"true\"` means UNATTENDED, which is the MORE restrictive mode here** — skip all prompts AND keep external/auth-gated materials record-only (never auto-attach them). It does NOT grant permission to attach external materials. Any other value (including `\"false\"`, missing, or empty) means an interactive invocation that MAY prompt for external/auth-gated materials. Invocations from `write-ticket` and `full-automation` are always unattended (`\"true\"`) for this step.\n\n## Instructions\n\n> **Orchestrator-directed step.** This agent task is authorized to call `attachment` (operations: `list`, `upload`) and `update_ticket_description` as directed below.\n\n1. **Read the record.** Read `{draft_file_path}` and parse its trailing `## Materials & Access` section. Collect the inventoried items grouped under *Reachable Local Files*, *External/Auth-Gated Links*, *Design/UI Comps (Fetchable)* (when present), and *Binary/Image Materials (Record-Only)*. If there is no `## Materials & Access` section, there is nothing to gather — return a no-op success.\n\n2. **Deduplicate first.** Call the `attachment` MCP tool with `operation` set to `\"list\"` and `ticket_number` set to `{ticket_number}` BEFORE uploading anything, so a resumed or re-run invocation does not re-attach a material that is already present. Compare against the deterministic filenames computed in step 4 and skip any that already exist.\n\n3. **Source classification (scheme-based, no network probe).** Honor the classification already recorded in the draft:\n - **Reachable Local Files** (local filesystem paths) are the only **low-risk** materials eligible for auto-attach — proceed to step 4.\n - **External/Auth-Gated Links** (every `http(s)` URI, even if explicitly linked) are **record-only** on unattended paths. If `{auto_approve_external}` is `\"true\"` (or the invocation is from `write-ticket` / `full-automation`), leave them record-only and never auto-attach. (Mind the polarity: `auto_approve_external = \"true\"` means we are in unattended mode, so external materials must stay record-only — `\"true\"` is NOT permission to attach them.) Only an explicitly interactive invocation (`auto_approve_external` is any non-`\"true\"` value) may prompt the user to confirm before attaching.\n - **Binary/Image Materials** are **record-only** in this step: the `attachment` tool's `upload` operation is UTF-8 text only, so never attempt a binary upload. (Binary-upload capability is the fast-follow sibling ticket BAPI-424.)\n - **Design/UI Comps (Fetchable)** are **not uploaded by this step**. A fetchable design/UI comp is a reference (an `attachment_id` or path) to a comp that already lives on the Jira ticket (or is fetched at implementation time); this gather step neither re-uploads it nor UTF-8-encodes its bytes. Leave each such comp reference recorded with its `attachment_id`/path in the `## Materials & Access` record so a later implementation agent can download it into its worktree via the Jira attachment download capability.\n\n4. **Gather and size-tier each reachable local text material.** For each low-risk local text material:\n - Read the local file from disk.\n - If the content exceeds **200,000 characters**, SKIP the upload and RECORD it (note the path and that it was skipped for size) — do not attach it.\n - If the content is **<= 200,000 characters**, upload it RAW via `attachment` (operation: `\"upload\"`). Do NOT summarize locally: the backend already summarizes attached text at plan time, so the size tiers are backend behavior this step defers to. The agent performs NO local summarization.\n - Use a deterministic, sanitized filename of the form `{ticket_number}-material-{hash}.md` (using the `{ticket_number}` input from the Inputs section), where `{hash}` is the first 8 hex characters of the SHA-256 digest of the sanitized absolute source path. Pin this algorithm exactly (SHA-256, first 8 hex chars, of the sanitized absolute path) — do NOT substitute another hash — so the same source always maps to the same filename and the dedup in step 2 works across separate sessions and re-runs. Keep the sanitized source provenance inside the attachment body, not only in the filename.\n\n5. **`attachment` upload parameter discipline (Zod).** When calling `attachment` with `operation: \"upload\"`, pass `ticket_number`, the attachment filename, and the text content. OMIT the optional parameters `link_type` and `replace_existing` entirely when they are unused — do NOT pass `null` or empty strings for them. The Zod schemas reject `null`/empty values, so an unused optional parameter must be omitted rather than nulled.\n\n6. **Redact secrets everywhere.** Before writing any URL or access note ANYWHERE — the Jira `## Materials & Access` record, any warning or final-report output, and any local intermediate file — sanitize and redact embedded credentials, SAS tokens, API keys, and basic-auth secrets using a high-visibility placeholder such as `[REDACTED_TOKEN]`. Mirror the backend `_redact_forge_fields()` / `_sanitize_jira_error_message()` patterns. A location/access note must never expose a plaintext secret.\n\n7. **Warn, never halt (error handling).** This step must NEVER halt, prompt-to-fail, or fail the overarching command because a material could not be gathered or attached. Follow the warn-not-halt convention:\n - If an `attachment` upload call fails (or a file disappeared between inventory and upload), warn gracefully and continue with the next material.\n - On such a post-create attach failure, call `update_ticket_description` to record the failure in the issue's `## Materials & Access` record (the material became unavailable only after the issue existed). `update_ticket_description` is an existing MCP tool, not a backend change.\n - Everything knowable PRE-CREATE was already written into the description at create time, so `update_ticket_description` is reserved for these rarer post-create attach failures. This complements the existing `partial_success` recording convention in `upload-and-track.md`.\n - Apply the step 6 redaction to every warning and recorded note.\n\n## Return\n\nConfirm the outcome: which local materials were attached (with their deterministic filenames), which were skipped/recorded (over-size, external/auth-gated, or binary/image), any attach failures recorded via `update_ticket_description`, and that no failure halted the run.\n",
|
|
706
706
|
"get-prd.md": "# get_prd\n\nRetrieve an already-generated **Product Requirements Document (PRD)** for a Jira\nticket.\n\nThis tool only **fetches** an existing PRD — it does **not** start or trigger\ngeneration. If no PRD exists yet (or you need a fresh one), call `request_prd`\nfirst; it starts the async generation and `get_prd` retrieves the result once\nprocessing completes.\n\nThe PRD is product/stakeholder-facing: problem framing, goals, non-goals, target\nusers, success metrics, product requirements, scope, and risks. Present the\nreturned markdown verbatim without summarizing.\n\n## Parameters\n\n| Parameter | Type | Default | Description |\n| --- | --- | --- | --- |\n| `ticket_number` | string | — | Jira ticket key in `PROJECT-NUMBER` format (e.g. `BAPI-123`). |\n| `save_locally` | boolean | `true` | Save the retrieved PRD to a local file. Set to `false` to skip saving. |\n\nLocal saves go to `BAPI_DOCS_DIR/prd/{ticket}-prd-plan.md`.\n\n## Return\n\n- The full PRD as markdown text when one exists.\n- A `404` / not-found response when no PRD is ready yet — that means generation\n has not run, not that the tool failed. Call `request_prd` to generate one.\n",
|
|
707
707
|
"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\n## Return\n\nReturn a brief summary of what was learned about the project's architecture (core principles, directory conventions, data flow), citing the key files inspected, and confirm the draft was written to `{docs_dir}/standards/architecture_instructions.md`.\n",
|
|
708
708
|
"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**: Search by filename pattern for 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\n## Return\n\nReturn a brief summary of what was learned about backend correctness conventions (structure, naming, error handling, auth, DB patterns), citing the key files inspected, and confirm the draft was written to `{docs_dir}/standards/backend_correctness_standards.md`.\n",
|