@bridge_gpt/mcp-server 0.2.49 → 0.2.50
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 +24 -7
- package/build/base-ref.js +28 -3
- package/build/claude-review-workflow-drift-probe.js +130 -0
- package/build/claude-review-workflow-drift.js +173 -0
- package/build/claude-review-workflow.js +81 -16
- package/build/commands.generated.js +5 -5
- package/build/conductor/done-gate.js +25 -3
- package/build/conductor/install-doctor.js +65 -5
- package/build/conductor/latest-check-selector.js +170 -0
- package/build/conductor/local-merge.js +8 -6
- package/build/conductor-bin.js +1 -1
- package/build/{brainstorm-files.js → council-files.js} +15 -15
- package/build/decision-page-schema.js +1 -1
- package/build/docs.generated.js +1 -1
- package/build/doctor.js +162 -4
- package/build/executor/worktree.js +46 -1
- package/build/index.js +92 -51
- package/build/init.js +9 -2
- package/build/install-bridge.js +60 -2
- package/build/install-reexec.js +47 -9
- package/build/pipelines.generated.js +1 -1
- package/build/plane/cli.js +12 -2
- package/build/plane/manifest.js +25 -1
- package/build/plane/member-roster.js +61 -7
- package/build/plane/preflight.js +24 -9
- package/build/plane/supervisor.js +77 -5
- package/build/plane/types.js +23 -3
- package/build/readme.generated.js +1 -1
- package/build/run-unit-tests-launcher.js +2 -1
- package/build/stale-worktree-doctor.js +120 -0
- package/build/start-tickets-prereqs.js +70 -0
- package/build/start-tickets.js +91 -3
- package/build/version.generated.js +3 -2
- package/package.json +4 -2
- package/build/chain-orchestrator.js +0 -1457
- package/build/chain-utils.js +0 -68
- package/build/command-catalog.js +0 -376
- package/build/schedule-run.js +0 -1300
- package/build/schedule-store.js +0 -172
- package/build/scheduled-prompt.js +0 -115
- package/build/scheduler-backends/at-fallback.js +0 -139
- package/build/scheduler-backends/escaping.js +0 -143
- package/build/scheduler-backends/index.js +0 -72
- package/build/scheduler-backends/launchd.js +0 -225
- package/build/scheduler-backends/systemd-user.js +0 -250
- package/build/scheduler-backends/task-scheduler.js +0 -214
- package/build/scheduler-backends/types.js +0 -23
|
@@ -1,214 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Windows Task Scheduler backend (BAPI-327).
|
|
3
|
-
*
|
|
4
|
-
* Generates a `.cmd` wrapper (baked PATH + env, `cd /D <repo>`, absolute Claude
|
|
5
|
-
* invocation, stdout/stderr redirection) plus a Task Scheduler `.xml` definition,
|
|
6
|
-
* and registers a one-shot task via `schtasks /Create /XML`.
|
|
7
|
-
*
|
|
8
|
-
* Why XML instead of `/SC ONCE /SD /ST`: the CLI `/SD` date is parsed in the
|
|
9
|
-
* host's short-date locale, so a hard-coded `MM/DD/YYYY` fails on UK/DE/JP/etc.
|
|
10
|
-
* machines. The XML `<StartBoundary>` is ISO-8601 and locale-independent.
|
|
11
|
-
*
|
|
12
|
-
* Drift policy: the generated XML omits the missed-start catch-up element (its
|
|
13
|
-
* schema default is already off), giving a real OS-level no-catch-up guarantee —
|
|
14
|
-
* a missed start never runs late. (launchd and `at` cannot guarantee this; the
|
|
15
|
-
* cross-backend staleness guard is the baked `--scheduled-at <T>` value checked by
|
|
16
|
-
* `/full-automation` in Phase C.) Working directory is handled only by `cd /D`
|
|
17
|
-
* inside the wrapper.
|
|
18
|
-
*/
|
|
19
|
-
import { promises as fs } from "node:fs";
|
|
20
|
-
import { pathApiForPlatform } from "./types.js";
|
|
21
|
-
import { bakedEnvFromCreateInput, bakedEnvEntries, escapeWindowsCmdSetValue, windowsCmdQuote, xmlEscape, } from "./escaping.js";
|
|
22
|
-
/** Task Scheduler task name for a NEW schedule id (neutral schedule-run naming). */
|
|
23
|
-
export function taskNameForId(id) {
|
|
24
|
-
return `BridgeGPT-ScheduleRun-${id}`;
|
|
25
|
-
}
|
|
26
|
-
/** `%USERPROFILE%\.bridge-gpt\schedules\<id>.cmd` wrapper path for a schedule id. */
|
|
27
|
-
export function cmdWrapperPathForId(id, homeDir) {
|
|
28
|
-
const pathApi = pathApiForPlatform("win32");
|
|
29
|
-
return pathApi.join(homeDir, ".bridge-gpt", "schedules", `${id}.cmd`);
|
|
30
|
-
}
|
|
31
|
-
/** `%USERPROFILE%\.bridge-gpt\schedules\<id>.xml` task definition path. */
|
|
32
|
-
export function taskXmlPathForId(id, homeDir) {
|
|
33
|
-
const pathApi = pathApiForPlatform("win32");
|
|
34
|
-
return pathApi.join(homeDir, ".bridge-gpt", "schedules", `${id}.xml`);
|
|
35
|
-
}
|
|
36
|
-
/**
|
|
37
|
-
* Local ISO-8601 `<StartBoundary>` value (`YYYY-MM-DDTHH:MM:SS`, no `Z`). Task
|
|
38
|
-
* Scheduler interprets a bare boundary as local time, preserving the prior
|
|
39
|
-
* local-time scheduling semantics while staying locale-independent.
|
|
40
|
-
*/
|
|
41
|
-
export function formatTaskSchedulerBoundary(runAtIso) {
|
|
42
|
-
const d = new Date(runAtIso);
|
|
43
|
-
const pad = (n) => String(n).padStart(2, "0");
|
|
44
|
-
return (`${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}` +
|
|
45
|
-
`T${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`);
|
|
46
|
-
}
|
|
47
|
-
/** Render the `.cmd` wrapper that the Task Scheduler task invokes. */
|
|
48
|
-
export function renderWindowsCmdWrapper(input) {
|
|
49
|
-
const env = bakedEnvEntries(bakedEnvFromCreateInput(input));
|
|
50
|
-
const lines = ["@echo off"];
|
|
51
|
-
for (const [key, value] of env) {
|
|
52
|
-
if (key === "PATH") {
|
|
53
|
-
// Prepend the baked PATH while preserving the runtime PATH behind it.
|
|
54
|
-
lines.push(`set "PATH=${escapeWindowsCmdSetValue(value)};%PATH%"`);
|
|
55
|
-
}
|
|
56
|
-
else {
|
|
57
|
-
lines.push(`set "${key}=${escapeWindowsCmdSetValue(value)}"`);
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
lines.push(`cd /D ${windowsCmdQuote(input.repoPath)}`);
|
|
61
|
-
// The wrapper runs the Node execution shim (trigger invocation), not the agent
|
|
62
|
-
// binary directly; the shim spawns the stored agent invocation.
|
|
63
|
-
const command = [input.triggerInvocation.exe, ...input.triggerInvocation.args]
|
|
64
|
-
.map((part) => windowsCmdQuote(part))
|
|
65
|
-
.join(" ");
|
|
66
|
-
// Append stdout/stderr to the local schedule logs.
|
|
67
|
-
lines.push(`${command} 1>>${windowsCmdQuote(input.paths.stdoutPath)} 2>>${windowsCmdQuote(input.paths.stderrPath)}`);
|
|
68
|
-
lines.push("");
|
|
69
|
-
return lines.join("\r\n");
|
|
70
|
-
}
|
|
71
|
-
/**
|
|
72
|
-
* Render a minimal one-shot Task Scheduler XML document. The single `TimeTrigger`
|
|
73
|
-
* fires once at the ISO-8601 `<StartBoundary>`; the action runs the generated
|
|
74
|
-
* `.cmd` wrapper. The `<Settings>` block intentionally does not enable the
|
|
75
|
-
* missed-start catch-up option, so a missed start never runs late.
|
|
76
|
-
*/
|
|
77
|
-
export function renderTaskSchedulerXml(input) {
|
|
78
|
-
const wrapperPath = cmdWrapperPathForId(input.id, input.deps.homeDir);
|
|
79
|
-
const boundary = formatTaskSchedulerBoundary(input.runAtIso);
|
|
80
|
-
return [
|
|
81
|
-
'<?xml version="1.0" encoding="UTF-16"?>',
|
|
82
|
-
'<Task version="1.2" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task">',
|
|
83
|
-
" <RegistrationInfo>",
|
|
84
|
-
` <Description>${xmlEscape(`Bridge GPT schedule-run one-shot (${input.id}: ${input.command})`)}</Description>`,
|
|
85
|
-
" </RegistrationInfo>",
|
|
86
|
-
" <Triggers>",
|
|
87
|
-
" <TimeTrigger>",
|
|
88
|
-
` <StartBoundary>${xmlEscape(boundary)}</StartBoundary>`,
|
|
89
|
-
" <Enabled>true</Enabled>",
|
|
90
|
-
" </TimeTrigger>",
|
|
91
|
-
" </Triggers>",
|
|
92
|
-
" <Principals>",
|
|
93
|
-
' <Principal id="Author">',
|
|
94
|
-
" <LogonType>InteractiveToken</LogonType>",
|
|
95
|
-
" </Principal>",
|
|
96
|
-
" </Principals>",
|
|
97
|
-
" <Settings>",
|
|
98
|
-
" <MultipleInstancesPolicy>IgnoreNew</MultipleInstancesPolicy>",
|
|
99
|
-
" <DisallowStartIfOnBatteries>false</DisallowStartIfOnBatteries>",
|
|
100
|
-
" <StopIfGoingOnBatteries>false</StopIfGoingOnBatteries>",
|
|
101
|
-
" <Enabled>true</Enabled>",
|
|
102
|
-
" </Settings>",
|
|
103
|
-
' <Actions Context="Author">',
|
|
104
|
-
" <Exec>",
|
|
105
|
-
` <Command>${xmlEscape(wrapperPath)}</Command>`,
|
|
106
|
-
" </Exec>",
|
|
107
|
-
" </Actions>",
|
|
108
|
-
"</Task>",
|
|
109
|
-
"",
|
|
110
|
-
].join("\r\n");
|
|
111
|
-
}
|
|
112
|
-
/** Whether `schtasks` is resolvable (used by isAvailable). */
|
|
113
|
-
async function schtasksResolvable(deps) {
|
|
114
|
-
const probe = await deps.runCommand("where.exe", ["schtasks"]);
|
|
115
|
-
return probe.exitCode === 0;
|
|
116
|
-
}
|
|
117
|
-
/** Create the Windows Task Scheduler backend. */
|
|
118
|
-
export function createTaskSchedulerBackend() {
|
|
119
|
-
return {
|
|
120
|
-
name: "task-scheduler",
|
|
121
|
-
async isAvailable(deps) {
|
|
122
|
-
if (deps.platform !== "win32")
|
|
123
|
-
return false;
|
|
124
|
-
return schtasksResolvable(deps);
|
|
125
|
-
},
|
|
126
|
-
async create(input) {
|
|
127
|
-
const wrapperPath = cmdWrapperPathForId(input.id, input.deps.homeDir);
|
|
128
|
-
const xmlPath = taskXmlPathForId(input.id, input.deps.homeDir);
|
|
129
|
-
const wrapperContent = renderWindowsCmdWrapper(input);
|
|
130
|
-
const xmlContent = renderTaskSchedulerXml(input);
|
|
131
|
-
const artifacts = [
|
|
132
|
-
{ path: wrapperPath, content: wrapperContent, kind: "windows-cmd-wrapper" },
|
|
133
|
-
{ path: xmlPath, content: xmlContent, kind: "task-scheduler-xml" },
|
|
134
|
-
];
|
|
135
|
-
const taskName = taskNameForId(input.id);
|
|
136
|
-
const unitPaths = [wrapperPath, xmlPath];
|
|
137
|
-
if (input.dryRun) {
|
|
138
|
-
return {
|
|
139
|
-
ok: true,
|
|
140
|
-
backend: "task-scheduler",
|
|
141
|
-
unitPath: wrapperPath,
|
|
142
|
-
unitPaths,
|
|
143
|
-
backendJobId: taskName,
|
|
144
|
-
artifacts,
|
|
145
|
-
};
|
|
146
|
-
}
|
|
147
|
-
await fs.writeFile(wrapperPath, wrapperContent, "utf-8");
|
|
148
|
-
// schtasks /XML requires the definition file to be UTF-16 with a BOM.
|
|
149
|
-
await fs.writeFile(xmlPath, `${xmlContent}`, "utf16le");
|
|
150
|
-
const result = await input.deps.runCommand("schtasks", [
|
|
151
|
-
"/Create",
|
|
152
|
-
"/TN",
|
|
153
|
-
taskName,
|
|
154
|
-
"/XML",
|
|
155
|
-
xmlPath,
|
|
156
|
-
"/F",
|
|
157
|
-
]);
|
|
158
|
-
if (result.exitCode !== 0) {
|
|
159
|
-
// Don't leave the orphaned wrapper + XML behind when registration fails.
|
|
160
|
-
await fs.unlink(wrapperPath).catch(() => undefined);
|
|
161
|
-
await fs.unlink(xmlPath).catch(() => undefined);
|
|
162
|
-
return {
|
|
163
|
-
ok: false,
|
|
164
|
-
backend: "task-scheduler",
|
|
165
|
-
unitPath: wrapperPath,
|
|
166
|
-
unitPaths,
|
|
167
|
-
backendJobId: taskName,
|
|
168
|
-
artifacts,
|
|
169
|
-
error: `schtasks /Create failed: ${(result.stderr || result.stdout).trim()}`,
|
|
170
|
-
};
|
|
171
|
-
}
|
|
172
|
-
return {
|
|
173
|
-
ok: true,
|
|
174
|
-
backend: "task-scheduler",
|
|
175
|
-
unitPath: wrapperPath,
|
|
176
|
-
unitPaths,
|
|
177
|
-
backendJobId: taskName,
|
|
178
|
-
artifacts,
|
|
179
|
-
};
|
|
180
|
-
},
|
|
181
|
-
async list(input) {
|
|
182
|
-
const entries = [];
|
|
183
|
-
for (const metadata of input.recorded) {
|
|
184
|
-
const taskName = metadata.backend_job_id ?? taskNameForId(metadata.id);
|
|
185
|
-
const query = await input.deps.runCommand("schtasks", ["/Query", "/TN", taskName]);
|
|
186
|
-
entries.push({
|
|
187
|
-
metadata,
|
|
188
|
-
status: query.exitCode === 0 ? "active" : "stale",
|
|
189
|
-
detail: query.exitCode === 0 ? "task registered" : "task not found",
|
|
190
|
-
});
|
|
191
|
-
}
|
|
192
|
-
return entries;
|
|
193
|
-
},
|
|
194
|
-
async cancel(input) {
|
|
195
|
-
const taskName = input.metadata.backend_job_id ?? taskNameForId(input.metadata.id);
|
|
196
|
-
const result = await input.deps.runCommand("schtasks", ["/Delete", "/TN", taskName, "/F"]);
|
|
197
|
-
// Remove the generated wrapper + XML definition; both are ENOENT-tolerant.
|
|
198
|
-
const wrapperPath = input.metadata.unit_path ?? cmdWrapperPathForId(input.metadata.id, input.deps.homeDir);
|
|
199
|
-
const xmlPath = taskXmlPathForId(input.metadata.id, input.deps.homeDir);
|
|
200
|
-
for (const artifactPath of [wrapperPath, xmlPath]) {
|
|
201
|
-
try {
|
|
202
|
-
await fs.unlink(artifactPath);
|
|
203
|
-
}
|
|
204
|
-
catch (error) {
|
|
205
|
-
if (error.code !== "ENOENT") {
|
|
206
|
-
return { ok: false, nativeRemoved: result.exitCode === 0, stale: false, error: String(error) };
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
const nativeRemoved = result.exitCode === 0;
|
|
211
|
-
return { ok: true, nativeRemoved, stale: !nativeRemoved };
|
|
212
|
-
},
|
|
213
|
-
};
|
|
214
|
-
}
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Shared contract for the cross-platform `schedule-run` scheduler backends
|
|
3
|
-
* (BAPI-327 / Phase B of the Full Automation v1 epic, BAPI-325).
|
|
4
|
-
*
|
|
5
|
-
* Every OS-native backend (launchd, Task Scheduler, systemd-user, at-fallback)
|
|
6
|
-
* implements the {@link SchedulerBackend} interface against an injected
|
|
7
|
-
* {@link SchedulerBackendDeps} so the create/list/cancel logic is fully unit
|
|
8
|
-
* testable without touching real schedulers, the filesystem, or the network.
|
|
9
|
-
*
|
|
10
|
-
* The design mirrors `start-tickets.ts`: a list-based {@link RunCommand} boundary
|
|
11
|
-
* (`execFile`, never `shell: true`), `platform`/`env`/`cwd`/`homeDir`/`execPath`
|
|
12
|
-
* captured as data, and an optional injectable clock for deterministic tests.
|
|
13
|
-
*/
|
|
14
|
-
import path from "node:path";
|
|
15
|
-
/**
|
|
16
|
-
* Return the platform-appropriate Node path API: `path.win32` on Windows so
|
|
17
|
-
* generated unit paths use backslashes, `path.posix` everywhere else. Using a
|
|
18
|
-
* data-driven platform (not the ambient `process.platform`) keeps generation
|
|
19
|
-
* deterministic and unit-testable for every OS from any host.
|
|
20
|
-
*/
|
|
21
|
-
export function pathApiForPlatform(platform) {
|
|
22
|
-
return platform === "win32" ? path.win32 : path.posix;
|
|
23
|
-
}
|