metheus-governance-mcp-cli 0.2.165 → 0.2.166

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
@@ -514,6 +514,8 @@ Workspace/source-of-truth model:
514
514
  - `project-workspaces.json` answers only one question: `project_id -> local workspace_dir on this machine`
515
515
  - use local tool `project.workspace` to read that binding
516
516
  - use local tool `project.workspace.bind` to explicitly update that binding
517
+ - use local tool `runner.project_up` to audit a project destination and create/select the executable runner routes
518
+ - use local tool `runner.show` to inspect one resolved route before launch
517
519
  - use local tool `runner.start_detached` to launch an existing runner route in a separate local terminal
518
520
  - use local tool `runner.status` to inspect detached runner processes launched by this CLI
519
521
  - use local tool `runner.stop` to stop detached runner processes launched by this CLI
@@ -587,6 +589,8 @@ Mapping behavior:
587
589
  - use `project.workspace` to read the current local binding from `project-workspaces.json`
588
590
  - use `project.workspace.bind` to intentionally write the binding into `project-workspaces.json`
589
591
  - `project.workspace.bind` updates the workspace registry directly; it does not need `bot-runner.json` side effects to succeed
592
+ - use `runner.project_up` after workspace binding to prepare executable routes for one project destination
593
+ - use `runner.show` to inspect the selected route before launching it
590
594
 
591
595
  Runner command contract:
592
596
  - stdin: JSON payload containing project, destination, trigger message, and recent context comments
package/cli.mjs CHANGED
@@ -4719,6 +4719,63 @@ function resolveRunnerProjectUpRoutes({
4719
4719
  }
4720
4720
 
4721
4721
  async function runRunnerProjectUp(flags) {
4722
+ const result = await buildRunnerProjectUpResult(flags);
4723
+ const { summaryPayload, applyRequested, applyResult, shouldStartRunner, matchingRoutes, startDetachedRequested, startFlags } = result;
4724
+ if (boolFromRaw(flags.json, false)) {
4725
+ process.stdout.write(`${JSON.stringify(summaryPayload, null, 2)}\n`);
4726
+ } else {
4727
+ process.stdout.write(
4728
+ [
4729
+ `Runner project up: ${summaryPayload.ok ? "OK" : "WARN"}`,
4730
+ `project_id: ${summaryPayload.project_id || "-"}`,
4731
+ `destination_label: ${summaryPayload.destination_label || "-"}`,
4732
+ `destination_id: ${summaryPayload.destination_id || "-"}`,
4733
+ `room_probe_ok: ${summaryPayload.room_probe_ok ? "true" : "false"}`,
4734
+ `room_visible_bot_admins: ${summaryPayload.room_visible_bot_admins}`,
4735
+ `my_server_bots: ${summaryPayload.my_server_bots}`,
4736
+ `my_local_bots: ${summaryPayload.my_local_bots}`,
4737
+ `bot_name_filter: ${summaryPayload.bot_name_filter}`,
4738
+ `bot_id_filter: ${summaryPayload.bot_id_filter}`,
4739
+ `role_filter: ${summaryPayload.role_filter}`,
4740
+ `route_suggestions_create_total: ${summaryPayload.route_suggestions_create_total}`,
4741
+ `route_suggestions_existing_total: ${summaryPayload.route_suggestions_existing_total}`,
4742
+ `route_suggestions_blocked_total: ${summaryPayload.route_suggestions_blocked_total}`,
4743
+ `route_apply_requested: ${summaryPayload.route_apply_requested ? "true" : "false"}`,
4744
+ `route_apply_changed: ${summaryPayload.route_apply_changed ? "true" : "false"}`,
4745
+ `route_config_file: ${summaryPayload.route_config_file}`,
4746
+ `enabled_routes_for_selection: ${summaryPayload.enabled_routes_for_selection.join(", ") || "-"}`,
4747
+ `start_requested: ${summaryPayload.start_requested ? "true" : "false"}`,
4748
+ `start_detached_requested: ${summaryPayload.start_detached_requested ? "true" : "false"}`,
4749
+ `next_steps: ${summaryPayload.next_steps.join(" | ") || "-"}`,
4750
+ ].join("\n") + "\n",
4751
+ );
4752
+ }
4753
+ if (applyRequested && applyResult.ok === false) {
4754
+ process.exitCode = 1;
4755
+ if (shouldStartRunner) {
4756
+ throw new Error(String(applyResult.error || "runner project up could not apply runner routes").trim());
4757
+ }
4758
+ return;
4759
+ }
4760
+ if (!shouldStartRunner) {
4761
+ return;
4762
+ }
4763
+ if (!matchingRoutes.length) {
4764
+ throw new Error("runner project up did not find any enabled routes for the selected project destination");
4765
+ }
4766
+ if (!boolFromRaw(flags.json, false)) {
4767
+ process.stdout.write(
4768
+ `${startDetachedRequested ? "Starting detached runner" : "Starting runner"} for project ${summaryPayload.project_id} destination ${summaryPayload.destination_label || summaryPayload.destination_id} using ${matchingRoutes.length} enabled route(s).\n`,
4769
+ );
4770
+ }
4771
+ if (startDetachedRequested) {
4772
+ await runRunnerStartDetachedResolvedRoutes(matchingRoutes, startFlags, "runner project up");
4773
+ return;
4774
+ }
4775
+ await runRunnerStartResolvedRoutes(matchingRoutes, startFlags);
4776
+ }
4777
+
4778
+ async function buildRunnerProjectUpResult(flags = {}) {
4722
4779
  const ui = createPrompter();
4723
4780
  try {
4724
4781
  if (shouldRenderPromptChrome(flags)) {
@@ -4776,6 +4833,7 @@ async function runRunnerProjectUp(flags) {
4776
4833
  changed: false,
4777
4834
  filePath: String(auditPayload.routeSuggestionConfigFilePath || "").trim(),
4778
4835
  appliedRoutes: [],
4836
+ disabledRoutes: [],
4779
4837
  error: "route apply skipped because the Telegram room visibility probe failed",
4780
4838
  })
4781
4839
  : null;
@@ -4820,6 +4878,8 @@ async function runRunnerProjectUp(flags) {
4820
4878
  start_requested: shouldStartRunner,
4821
4879
  start_detached_requested: startDetachedRequested,
4822
4880
  next_steps: [],
4881
+ applied_routes: ensureArray(applyResult.appliedRoutes).map((item) => safeObject(item)),
4882
+ disabled_routes: ensureArray(applyResult.disabledRoutes).map((item) => safeObject(item)),
4823
4883
  };
4824
4884
  if (!applyRequested) {
4825
4885
  summaryPayload.next_steps.push(`${CLI_NAME} runner project up --project-id ${summaryPayload.project_id} --provider ${provider} --destination-id ${summaryPayload.destination_id} --apply true --start false`);
@@ -4828,58 +4888,18 @@ async function runRunnerProjectUp(flags) {
4828
4888
  } else {
4829
4889
  summaryPayload.next_steps.push(`${CLI_NAME} runner show --route-name ${summaryPayload.enabled_routes_for_selection[0] || "<route_name>"}`);
4830
4890
  }
4831
- if (boolFromRaw(flags.json, false)) {
4832
- process.stdout.write(`${JSON.stringify(summaryPayload, null, 2)}\n`);
4833
- } else {
4834
- process.stdout.write(
4835
- [
4836
- `Runner project up: ${summaryPayload.ok ? "OK" : "WARN"}`,
4837
- `project_id: ${summaryPayload.project_id || "-"}`,
4838
- `destination_label: ${summaryPayload.destination_label || "-"}`,
4839
- `destination_id: ${summaryPayload.destination_id || "-"}`,
4840
- `room_probe_ok: ${summaryPayload.room_probe_ok ? "true" : "false"}`,
4841
- `room_visible_bot_admins: ${summaryPayload.room_visible_bot_admins}`,
4842
- `my_server_bots: ${summaryPayload.my_server_bots}`,
4843
- `my_local_bots: ${summaryPayload.my_local_bots}`,
4844
- `bot_name_filter: ${summaryPayload.bot_name_filter}`,
4845
- `bot_id_filter: ${summaryPayload.bot_id_filter}`,
4846
- `role_filter: ${summaryPayload.role_filter}`,
4847
- `route_suggestions_create_total: ${summaryPayload.route_suggestions_create_total}`,
4848
- `route_suggestions_existing_total: ${summaryPayload.route_suggestions_existing_total}`,
4849
- `route_suggestions_blocked_total: ${summaryPayload.route_suggestions_blocked_total}`,
4850
- `route_apply_requested: ${summaryPayload.route_apply_requested ? "true" : "false"}`,
4851
- `route_apply_changed: ${summaryPayload.route_apply_changed ? "true" : "false"}`,
4852
- `route_config_file: ${summaryPayload.route_config_file}`,
4853
- `enabled_routes_for_selection: ${summaryPayload.enabled_routes_for_selection.join(", ") || "-"}`,
4854
- `start_requested: ${summaryPayload.start_requested ? "true" : "false"}`,
4855
- `start_detached_requested: ${summaryPayload.start_detached_requested ? "true" : "false"}`,
4856
- `next_steps: ${summaryPayload.next_steps.join(" | ") || "-"}`,
4857
- ].join("\n") + "\n",
4858
- );
4859
- }
4860
- if (applyRequested && applyResult.ok === false) {
4861
- process.exitCode = 1;
4862
- if (shouldStartRunner) {
4863
- throw new Error(String(applyResult.error || "runner project up could not apply runner routes").trim());
4864
- }
4865
- return;
4866
- }
4867
- if (!shouldStartRunner) {
4868
- return;
4869
- }
4870
- if (!matchingRoutes.length) {
4871
- throw new Error("runner project up did not find any enabled routes for the selected project destination");
4872
- }
4873
- if (!boolFromRaw(flags.json, false)) {
4874
- process.stdout.write(
4875
- `${startDetachedRequested ? "Starting detached runner" : "Starting runner"} for project ${summaryPayload.project_id} destination ${summaryPayload.destination_label || summaryPayload.destination_id} using ${matchingRoutes.length} enabled route(s).\n`,
4876
- );
4877
- }
4878
- if (startDetachedRequested) {
4879
- await runRunnerStartDetachedResolvedRoutes(matchingRoutes, startFlags, "runner project up");
4880
- return;
4891
+ if (applyRequested && applyResult.ok === false && applyResult.error) {
4892
+ summaryPayload.error = String(applyResult.error || "").trim();
4881
4893
  }
4882
- await runRunnerStartResolvedRoutes(matchingRoutes, startFlags);
4894
+ return {
4895
+ summaryPayload,
4896
+ applyRequested,
4897
+ applyResult,
4898
+ shouldStartRunner,
4899
+ startDetachedRequested,
4900
+ matchingRoutes,
4901
+ startFlags,
4902
+ };
4883
4903
  } finally {
4884
4904
  ui.close();
4885
4905
  }
@@ -7314,6 +7334,9 @@ function buildLocalProjectDispatchDeps() {
7314
7334
  normalizeMergeAction,
7315
7335
  executeCtxpackMergeActionForTool: (params) => executeCtxpackMergeActionForTool(params, buildProjectToolDeps()),
7316
7336
  buildCtxpackMergeExecuteText,
7337
+ buildRunnerProjectUpResult: (flags) => buildRunnerProjectUpResult(flags),
7338
+ resolveRunnerShowSelection: (flags) => resolveRunnerShowSelection(flags),
7339
+ buildRunnerShowPayload: (route, flags) => buildRunnerShowPayload(route, flags),
7317
7340
  startDetachedRunnerWithFlags: (flags, sourceCommand) => startDetachedRunnerWithFlags(flags, sourceCommand),
7318
7341
  buildRunnerDetachedStatusPayload: (flags) => buildRunnerDetachedStatusPayload(flags),
7319
7342
  stopDetachedRunnerWithFlags: (flags) => stopDetachedRunnerWithFlags(flags),
@@ -7853,6 +7876,8 @@ const LOCAL_PROJECT_TOOL_NAMES = [
7853
7876
  "project.workspace.bind",
7854
7877
  ];
7855
7878
  const LOCAL_RUNNER_TOOL_NAMES = [
7879
+ "runner.project_up",
7880
+ "runner.show",
7856
7881
  "runner.start_detached",
7857
7882
  "runner.status",
7858
7883
  "runner.stop",
@@ -7997,6 +8022,46 @@ function buildRunnerStartDetachedInputSchema() {
7997
8022
  };
7998
8023
  }
7999
8024
 
8025
+ function buildRunnerProjectUpInputSchema() {
8026
+ return {
8027
+ type: "object",
8028
+ properties: {
8029
+ project_id: { type: "string", description: "Project UUID to audit and bind to a destination." },
8030
+ provider: { type: "string", enum: ["telegram"], description: "Provider. runner.project_up currently supports telegram only." },
8031
+ destination_id: { type: "string", description: "Destination UUID filter." },
8032
+ destination_label: { type: "string", description: "Destination label filter." },
8033
+ bot_name: { type: "string", description: "Server bot name filter." },
8034
+ bot_id: { type: "string", description: "Server bot UUID filter." },
8035
+ role: { type: "string", enum: ["monitor", "review", "worker", "approval"], description: "Single role filter." },
8036
+ roles: { type: "string", description: "Comma-separated role filter list." },
8037
+ apply: { type: "boolean", description: "If true, create/disable routes as suggested. Defaults to true." },
8038
+ start: { type: "boolean", description: "If true, start the resolved routes after apply." },
8039
+ start_detached: { type: "boolean", description: "If true, start in a separate local terminal after apply." },
8040
+ dry_run_delivery: { type: "boolean", description: "If true, do not actually send provider replies." },
8041
+ concurrency: { type: "integer", minimum: 1, description: "Max concurrent scheduling groups." },
8042
+ },
8043
+ required: ["project_id"],
8044
+ additionalProperties: false,
8045
+ };
8046
+ }
8047
+
8048
+ function buildRunnerShowInputSchema() {
8049
+ return {
8050
+ type: "object",
8051
+ properties: {
8052
+ route_name: { type: "string", description: "Exact runner route name." },
8053
+ bot_name: { type: "string", description: "Server bot name when one enabled route matches." },
8054
+ bot_id: { type: "string", description: "Server bot UUID when one enabled route matches." },
8055
+ project_id: { type: "string", description: "Project UUID filter." },
8056
+ provider: { type: "string", enum: ["telegram", "slack", "kakaotalk"], description: "Provider filter." },
8057
+ role: { type: "string", enum: ["monitor", "review", "worker", "approval"], description: "Role filter." },
8058
+ destination_id: { type: "string", description: "Destination UUID filter." },
8059
+ destination_label: { type: "string", description: "Destination label filter." },
8060
+ },
8061
+ additionalProperties: false,
8062
+ };
8063
+ }
8064
+
8000
8065
  function buildRunnerStatusInputSchema() {
8001
8066
  return {
8002
8067
  type: "object",
@@ -8056,6 +8121,18 @@ function buildLocalToolSpecs() {
8056
8121
  "Bind a project_id to a local workspace_dir in project-workspaces.json so local runner and AI execution use the correct project folder.",
8057
8122
  inputSchema: buildProjectWorkspaceBindInputSchema(),
8058
8123
  },
8124
+ {
8125
+ name: "runner.project_up",
8126
+ description:
8127
+ "Audit one project destination, create missing runner routes, and optionally start polling. Use this before runner.start_detached when the route is not ready yet.",
8128
+ inputSchema: buildRunnerProjectUpInputSchema(),
8129
+ },
8130
+ {
8131
+ name: "runner.show",
8132
+ description:
8133
+ "Inspect one resolved runner route and report workspace mapping, execution profile, and last run details.",
8134
+ inputSchema: buildRunnerShowInputSchema(),
8135
+ },
8059
8136
  {
8060
8137
  name: "runner.start_detached",
8061
8138
  description:
@@ -8508,6 +8585,8 @@ function appendProjectHintToInitialize(responseObj, args, options = {}) {
8508
8585
  const projectWorkspaceTool = displayToolNameForClient("project.workspace", useSafeToolAliases);
8509
8586
  const projectWorkspaceBindTool = displayToolNameForClient("project.workspace.bind", useSafeToolAliases);
8510
8587
  const projectSummaryTool = displayToolNameForClient("project.summary", useSafeToolAliases);
8588
+ const runnerProjectUpTool = displayToolNameForClient("runner.project_up", useSafeToolAliases);
8589
+ const runnerShowTool = displayToolNameForClient("runner.show", useSafeToolAliases);
8511
8590
  const runnerStartDetachedTool = displayToolNameForClient("runner.start_detached", useSafeToolAliases);
8512
8591
  const runnerStatusTool = displayToolNameForClient("runner.status", useSafeToolAliases);
8513
8592
  const runnerStopTool = displayToolNameForClient("runner.stop", useSafeToolAliases);
@@ -8528,6 +8607,7 @@ function appendProjectHintToInitialize(responseObj, args, options = {}) {
8528
8607
  : []),
8529
8608
  `- MUST call \`${projectWorkspaceTool}\` first when the user provides only a Project ID and you need the local project folder/workspace binding.`,
8530
8609
  `- Use \`${projectWorkspaceBindTool}\` only when you need to explicitly write/update the local project-workspaces.json registry binding.`,
8610
+ `- Use \`${runnerProjectUpTool}\` to audit a project destination and create/select runner routes before starting polling. Then use \`${runnerShowTool}\` to inspect the resolved route if needed.`,
8531
8611
  `- To run automated polling independently of the current AI session, call \`${runnerStartDetachedTool}\` after the workspace binding and route are ready. Use \`${runnerStatusTool}\` to inspect detached runner processes and \`${runnerStopTool}\` to stop them later.`,
8532
8612
  `- After resolving the local workspace, call \`${projectSummaryTool}\` for project overview/agenda/server metadata.`,
8533
8613
  `- If user enters a Project ID in text (e.g., "Project ID <uuid>"), pass that exact UUID as \`project_id\` argument when calling \`${projectSummaryTool}\`.`,
@@ -9732,12 +9812,125 @@ TELEGRAM_BOT_REVIEW_TOKEN=review-token
9732
9812
  push("detached_runner_linux_launcher_selects_supported_terminal", false, String(err?.message || err));
9733
9813
  }
9734
9814
 
9815
+ try {
9816
+ const projectUpResponse = await handleLocalProjectToolDispatchImpl(
9817
+ {
9818
+ requestObj: {
9819
+ jsonrpc: "2.0",
9820
+ id: 8,
9821
+ method: "tools/call",
9822
+ params: {
9823
+ name: "runner.project_up",
9824
+ arguments: {
9825
+ project_id: selftestProjectID,
9826
+ destination_label: "Selftest Room",
9827
+ },
9828
+ },
9829
+ },
9830
+ toolName: "runner.project_up",
9831
+ toolArgs: {
9832
+ project_id: selftestProjectID,
9833
+ destination_label: "Selftest Room",
9834
+ },
9835
+ args: {
9836
+ baseURL: DEFAULT_SITE_URL,
9837
+ timeoutSeconds: 30,
9838
+ },
9839
+ token: "selftest-token",
9840
+ workspaceDir: "",
9841
+ workspaceSignalTrusted: true,
9842
+ },
9843
+ {
9844
+ ...buildLocalProjectDispatchDeps(),
9845
+ buildRunnerProjectUpResult: async () => ({
9846
+ summaryPayload: {
9847
+ ok: true,
9848
+ project_id: selftestProjectID,
9849
+ destination_label: "Selftest Room",
9850
+ destination_id: "dest-1",
9851
+ route_apply_requested: true,
9852
+ route_apply_changed: true,
9853
+ enabled_routes_for_selection: ["telegram-monitor-selftest"],
9854
+ next_steps: [`${CLI_NAME} runner show --route-name telegram-monitor-selftest`],
9855
+ },
9856
+ matchingRoutes: [{ name: "telegram-monitor-selftest" }],
9857
+ applyRequested: true,
9858
+ applyResult: { ok: true, changed: true },
9859
+ shouldStartRunner: false,
9860
+ startDetachedRequested: false,
9861
+ startFlags: {},
9862
+ }),
9863
+ },
9864
+ );
9865
+ const projectUpStructured = safeObject(safeObject(projectUpResponse).result)?.structuredContent || {};
9866
+ push(
9867
+ "local_runner_project_up_tool_dispatches",
9868
+ projectUpStructured.ok === true
9869
+ && ensureArray(projectUpStructured.enabled_routes_for_selection).includes("telegram-monitor-selftest"),
9870
+ `routes=${ensureArray(projectUpStructured.enabled_routes_for_selection).join(",")} next=${ensureArray(projectUpStructured.next_steps).join(" | ")}`,
9871
+ );
9872
+ } catch (err) {
9873
+ push("local_runner_project_up_tool_dispatches", false, String(err?.message || err));
9874
+ }
9875
+
9876
+ try {
9877
+ const showResponse = await handleLocalProjectToolDispatchImpl(
9878
+ {
9879
+ requestObj: {
9880
+ jsonrpc: "2.0",
9881
+ id: 9,
9882
+ method: "tools/call",
9883
+ params: {
9884
+ name: "runner.show",
9885
+ arguments: {
9886
+ route_name: "telegram-monitor-selftest",
9887
+ },
9888
+ },
9889
+ },
9890
+ toolName: "runner.show",
9891
+ toolArgs: {
9892
+ route_name: "telegram-monitor-selftest",
9893
+ },
9894
+ args: {
9895
+ baseURL: DEFAULT_SITE_URL,
9896
+ timeoutSeconds: 30,
9897
+ },
9898
+ token: "selftest-token",
9899
+ workspaceDir: "",
9900
+ workspaceSignalTrusted: true,
9901
+ },
9902
+ {
9903
+ ...buildLocalProjectDispatchDeps(),
9904
+ resolveRunnerShowSelection: () => ({ name: "telegram-monitor-selftest" }),
9905
+ buildRunnerShowPayload: () => ({
9906
+ ok: true,
9907
+ route_name: "telegram-monitor-selftest",
9908
+ route_config: { project_id: selftestProjectID },
9909
+ resolved_destination: { destination_label: "Selftest Room" },
9910
+ workspace_mapping: { workspace_dir: "C:\\selftest-workspace", workspace_source: "project-workspaces.json:manual_override" },
9911
+ execution_profile: { permission_mode: "read_only" },
9912
+ warnings: [],
9913
+ errors: [],
9914
+ }),
9915
+ },
9916
+ );
9917
+ const showStructured = safeObject(safeObject(showResponse).result)?.structuredContent || {};
9918
+ push(
9919
+ "local_runner_show_tool_dispatches",
9920
+ showStructured.ok === true
9921
+ && String(showStructured.route_name || "").trim() === "telegram-monitor-selftest",
9922
+ `route=${String(showStructured.route_name || "").trim() || "(none)"} workspace=${String(safeObject(showStructured.workspace_mapping).workspace_dir || "").trim() || "-"}`,
9923
+ );
9924
+ } catch (err) {
9925
+ push("local_runner_show_tool_dispatches", false, String(err?.message || err));
9926
+ }
9927
+
9735
9928
  try {
9736
9929
  const startResponse = await handleLocalProjectToolDispatchImpl(
9737
9930
  {
9738
9931
  requestObj: {
9739
9932
  jsonrpc: "2.0",
9740
- id: 5,
9933
+ id: 10,
9741
9934
  method: "tools/call",
9742
9935
  params: {
9743
9936
  name: "runner.start_detached",
@@ -9791,7 +9984,7 @@ TELEGRAM_BOT_REVIEW_TOKEN=review-token
9791
9984
  {
9792
9985
  requestObj: {
9793
9986
  jsonrpc: "2.0",
9794
- id: 6,
9987
+ id: 11,
9795
9988
  method: "tools/call",
9796
9989
  params: {
9797
9990
  name: "runner.status",
@@ -9843,7 +10036,7 @@ TELEGRAM_BOT_REVIEW_TOKEN=review-token
9843
10036
  {
9844
10037
  requestObj: {
9845
10038
  jsonrpc: "2.0",
9846
- id: 7,
10039
+ id: 12,
9847
10040
  method: "tools/call",
9848
10041
  params: {
9849
10042
  name: "runner.stop",
@@ -57,6 +57,9 @@ export async function handleLocalProjectToolDispatch(
57
57
  const normalizeMergeAction = requireDependency(deps, "normalizeMergeAction");
58
58
  const executeCtxpackMergeActionForTool = requireDependency(deps, "executeCtxpackMergeActionForTool");
59
59
  const buildCtxpackMergeExecuteText = requireDependency(deps, "buildCtxpackMergeExecuteText");
60
+ const buildRunnerProjectUpResult = requireDependency(deps, "buildRunnerProjectUpResult");
61
+ const resolveRunnerShowSelection = requireDependency(deps, "resolveRunnerShowSelection");
62
+ const buildRunnerShowPayload = requireDependency(deps, "buildRunnerShowPayload");
60
63
  const startDetachedRunnerWithFlags = requireDependency(deps, "startDetachedRunnerWithFlags");
61
64
  const buildRunnerDetachedStatusPayload = requireDependency(deps, "buildRunnerDetachedStatusPayload");
62
65
  const stopDetachedRunnerWithFlags = requireDependency(deps, "stopDetachedRunnerWithFlags");
@@ -241,6 +244,53 @@ export async function handleLocalProjectToolDispatch(
241
244
 
242
245
  if (localRunnerToolNames.includes(toolName)) {
243
246
  try {
247
+ if (toolName === "runner.project_up") {
248
+ const normalizedFlags = {
249
+ ...toolArgs,
250
+ ...(Object.prototype.hasOwnProperty.call(toolArgs, "project_id") ? {} : { project_id: resolveProjectID(toolArgs, args, workspaceDir, deps) }),
251
+ ...(Object.prototype.hasOwnProperty.call(toolArgs, "start") ? {} : { start: false }),
252
+ ...(Object.prototype.hasOwnProperty.call(toolArgs, "start_detached") ? {} : { start_detached: false }),
253
+ ...(Object.prototype.hasOwnProperty.call(toolArgs, "json") ? {} : { json: true }),
254
+ };
255
+ const result = await buildRunnerProjectUpResult(normalizedFlags);
256
+ const payload = {
257
+ ...safeObject(result.summaryPayload),
258
+ matching_routes: ensureArray(result.matchingRoutes).map((route) => String(safeObject(route).name || "").trim()).filter(Boolean),
259
+ };
260
+ const text = [
261
+ `ok: ${payload.ok ? "true" : "false"}`,
262
+ `project_id: ${String(payload.project_id || "").trim() || "-"}`,
263
+ `destination_label: ${String(payload.destination_label || "").trim() || "-"}`,
264
+ `destination_id: ${String(payload.destination_id || "").trim() || "-"}`,
265
+ `route_apply_requested: ${payload.route_apply_requested ? "true" : "false"}`,
266
+ `route_apply_changed: ${payload.route_apply_changed ? "true" : "false"}`,
267
+ `enabled_routes_for_selection: ${ensureArray(payload.enabled_routes_for_selection).join(", ") || "-"}`,
268
+ `next_steps: ${ensureArray(payload.next_steps).join(" | ") || "-"}`,
269
+ ].join("\n");
270
+ return jsonRpcResult(requestObj, {
271
+ content: [{ type: "text", text }],
272
+ structuredContent: payload,
273
+ });
274
+ }
275
+ if (toolName === "runner.show") {
276
+ const route = resolveRunnerShowSelection(toolArgs);
277
+ const payload = buildRunnerShowPayload(route, toolArgs);
278
+ const text = [
279
+ `ok: ${payload.ok ? "true" : "false"}`,
280
+ `route_name: ${String(payload.route_name || "").trim() || "-"}`,
281
+ `project_id: ${String(safeObject(payload.route_config).project_id || "").trim() || "-"}`,
282
+ `destination_label: ${String(safeObject(payload.resolved_destination).destination_label || "").trim() || "-"}`,
283
+ `workspace_dir: ${String(safeObject(payload.workspace_mapping).workspace_dir || "").trim() || "-"}`,
284
+ `workspace_source: ${String(safeObject(payload.workspace_mapping).workspace_source || "").trim() || "-"}`,
285
+ `permission_mode: ${String(safeObject(payload.execution_profile).permission_mode || "").trim() || "-"}`,
286
+ `warnings: ${ensureArray(payload.warnings).join(" | ") || "-"}`,
287
+ `errors: ${ensureArray(payload.errors).join(" | ") || "-"}`,
288
+ ].join("\n");
289
+ return jsonRpcResult(requestObj, {
290
+ content: [{ type: "text", text }],
291
+ structuredContent: payload,
292
+ });
293
+ }
244
294
  if (toolName === "runner.start_detached") {
245
295
  const payload = await startDetachedRunnerWithFlags(toolArgs, "runner.start_detached");
246
296
  const launch = safeObject(payload.launch);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "metheus-governance-mcp-cli",
3
- "version": "0.2.165",
3
+ "version": "0.2.166",
4
4
  "description": "Metheus Governance MCP CLI (setup + stdio proxy)",
5
5
  "type": "module",
6
6
  "files": [