metheus-governance-mcp-cli 0.2.4 → 0.2.6
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/cli.mjs +138 -24
- package/package.json +1 -1
package/cli.mjs
CHANGED
|
@@ -26,12 +26,12 @@ function printUsage() {
|
|
|
26
26
|
"Metheus Governance MCP CLI",
|
|
27
27
|
"",
|
|
28
28
|
"Usage:",
|
|
29
|
-
" metheus-governance-mcp [--project-id <uuid>] [--ctxpack-key <key>] [--base-url <url>] [--flow <auto|device|callback|manual>]",
|
|
29
|
+
" metheus-governance-mcp [--project-id <uuid>] [--ctxpack-key <key>] [--base-url <url>] [--workspace-dir <path>] [--flow <auto|device|callback|manual>]",
|
|
30
30
|
" metheus-governance-mcp init [--project-id <uuid>] [--ctxpack-key <key>] [--base-url <url>] [--flow <auto|device|callback|manual>]",
|
|
31
|
-
" metheus-governance-mcp setup [--project-id <uuid>] [--ctxpack-key <key>] [--base-url <url>] [--name <server_name>]",
|
|
31
|
+
" metheus-governance-mcp setup [--project-id <uuid>] [--ctxpack-key <key>] [--base-url <url>] [--workspace-dir <path>] [--name <server_name>]",
|
|
32
32
|
" metheus-governance-mcp doctor [--project-id <uuid>] [--ctxpack-key <key>] [--base-url <url>] [--timeout-seconds <n>]",
|
|
33
|
-
" metheus-governance-mcp proxy [--project-id <uuid>] [--ctxpack-key <key>] [--base-url <url>] [--include-drafts <true|false>] [--timeout-seconds <n>]",
|
|
34
|
-
" metheus-governance-mcp ctxpack pull [--project-id <uuid>] [--base-url <url>] [--timeout-seconds <n>]",
|
|
33
|
+
" metheus-governance-mcp proxy [--project-id <uuid>] [--ctxpack-key <key>] [--base-url <url>] [--workspace-dir <path>] [--include-drafts <true|false>] [--timeout-seconds <n>]",
|
|
34
|
+
" metheus-governance-mcp ctxpack pull [--project-id <uuid>] [--base-url <url>] [--workspace-dir <path>] [--timeout-seconds <n>]",
|
|
35
35
|
" metheus-governance-mcp auth status",
|
|
36
36
|
" metheus-governance-mcp auth login [--base-url <url>] [--flow <auto|device|callback|manual>] [--keycloak-url <url>] [--realm <name>] [--client-id <id>] [--open-browser <true|false>] [--callback-port <n>] [--timeout-seconds <n>] [--manual <true|false>]",
|
|
37
37
|
" metheus-governance-mcp auth set --token <jwt> [--refresh-token <token>] [--base-url <url>]",
|
|
@@ -101,7 +101,15 @@ function authStoreFilePath() {
|
|
|
101
101
|
return path.join(home, AUTH_STORE_RELATIVE_PATH);
|
|
102
102
|
}
|
|
103
103
|
|
|
104
|
-
function
|
|
104
|
+
function resolveWorkspaceDir(rawPath) {
|
|
105
|
+
const input = String(rawPath || "").trim();
|
|
106
|
+
if (input) {
|
|
107
|
+
return path.resolve(input);
|
|
108
|
+
}
|
|
109
|
+
return path.resolve(process.cwd());
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function homeCtxpackCacheRootDir() {
|
|
105
113
|
const home = String(process.env.USERPROFILE || process.env.HOME || "").trim();
|
|
106
114
|
if (!home) {
|
|
107
115
|
return path.resolve(process.cwd(), CTXPACK_CACHE_RELATIVE_DIR);
|
|
@@ -109,6 +117,14 @@ function ctxpackCacheRootDir() {
|
|
|
109
117
|
return path.join(home, CTXPACK_CACHE_RELATIVE_DIR);
|
|
110
118
|
}
|
|
111
119
|
|
|
120
|
+
function ctxpackCacheRootDir(workspaceDir) {
|
|
121
|
+
const root = resolveWorkspaceDir(workspaceDir);
|
|
122
|
+
if (!root) {
|
|
123
|
+
return homeCtxpackCacheRootDir();
|
|
124
|
+
}
|
|
125
|
+
return path.join(root, CTXPACK_CACHE_RELATIVE_DIR);
|
|
126
|
+
}
|
|
127
|
+
|
|
112
128
|
function ensureParentDir(filePath) {
|
|
113
129
|
const parent = path.dirname(filePath);
|
|
114
130
|
fs.mkdirSync(parent, { recursive: true });
|
|
@@ -1241,6 +1257,7 @@ async function resolveAccessTokenForCommand(baseURL, timeoutSeconds) {
|
|
|
1241
1257
|
async function runCtxpackPull(flags) {
|
|
1242
1258
|
const workspaceMeta = loadWorkspaceMeta(process.cwd());
|
|
1243
1259
|
const projectID = String(flags["project-id"] || workspaceMeta.project_id || "").trim();
|
|
1260
|
+
const workspaceDir = resolveWorkspaceDir(flags["workspace-dir"] || process.cwd());
|
|
1244
1261
|
if (!projectID) {
|
|
1245
1262
|
process.stderr.write("Missing project_id. Use --project-id <uuid> or run inside a synced ctxpack workspace.\n");
|
|
1246
1263
|
process.exitCode = 1;
|
|
@@ -1271,6 +1288,7 @@ async function runCtxpackPull(flags) {
|
|
|
1271
1288
|
timeoutSeconds,
|
|
1272
1289
|
includeCtxpack: true,
|
|
1273
1290
|
syncCtxpackLocal: true,
|
|
1291
|
+
workspaceDir,
|
|
1274
1292
|
});
|
|
1275
1293
|
process.stdout.write(`${buildProjectSummaryText(summary)}\n`);
|
|
1276
1294
|
|
|
@@ -1457,6 +1475,7 @@ async function runDoctor(flags) {
|
|
|
1457
1475
|
timeoutSeconds,
|
|
1458
1476
|
includeCtxpack: true,
|
|
1459
1477
|
syncCtxpackLocal: true,
|
|
1478
|
+
workspaceDir: context.workspaceDir,
|
|
1460
1479
|
});
|
|
1461
1480
|
if (String(summary.access || "") !== "granted") {
|
|
1462
1481
|
addDoctorCheck(
|
|
@@ -1832,19 +1851,22 @@ function hasAllCtxpackFiles(baseDir, files) {
|
|
|
1832
1851
|
return true;
|
|
1833
1852
|
}
|
|
1834
1853
|
|
|
1835
|
-
function syncCtxpackToLocalCache({ siteBaseURL, projectID, ctxpack }) {
|
|
1854
|
+
function syncCtxpackToLocalCache({ siteBaseURL, projectID, ctxpack, workspaceDir }) {
|
|
1836
1855
|
const ctxpackID = String(ctxpack?.ctxpack_id || "").trim();
|
|
1837
1856
|
const version = String(ctxpack?.version || "").trim();
|
|
1838
1857
|
const status = String(ctxpack?.status || "").trim() || "draft";
|
|
1839
1858
|
const files = normalizeCtxpackFiles(ctxpack?.files);
|
|
1840
|
-
const
|
|
1859
|
+
const resolvedWorkspaceDir = resolveWorkspaceDir(workspaceDir);
|
|
1860
|
+
const cacheDir = path.join(ctxpackCacheRootDir(resolvedWorkspaceDir), projectID);
|
|
1841
1861
|
const metaPath = path.join(cacheDir, CTXPACK_META_FILENAME);
|
|
1862
|
+
const workspaceMetaPath = path.join(resolvedWorkspaceDir, CTXPACK_META_FILENAME);
|
|
1842
1863
|
|
|
1843
1864
|
if (!ctxpackID || !version || files.length === 0) {
|
|
1844
1865
|
return {
|
|
1845
1866
|
sync_status: "not_available",
|
|
1846
1867
|
sync_message: "Ctxpack is missing or has no files on server.",
|
|
1847
1868
|
local_path: cacheDir,
|
|
1869
|
+
workspace_path: resolvedWorkspaceDir,
|
|
1848
1870
|
local_file_count: 0,
|
|
1849
1871
|
};
|
|
1850
1872
|
}
|
|
@@ -1854,12 +1876,28 @@ function syncCtxpackToLocalCache({ siteBaseURL, projectID, ctxpack }) {
|
|
|
1854
1876
|
const previousSig = previousMeta
|
|
1855
1877
|
? `${String(previousMeta.ctxpack_id || "").trim()}|${String(previousMeta.version || "").trim()}|${String(previousMeta.status || "").trim()}|${Number.parseInt(String(previousMeta.files_count || 0), 10) || 0}`
|
|
1856
1878
|
: "";
|
|
1879
|
+
const payload = {
|
|
1880
|
+
project_id: projectID,
|
|
1881
|
+
ctxpack_id: ctxpackID,
|
|
1882
|
+
version,
|
|
1883
|
+
status,
|
|
1884
|
+
files_count: files.length,
|
|
1885
|
+
files: files.map((f) => ({ path: f.path, doc_type: f.docType || "" })),
|
|
1886
|
+
source: `${String(siteBaseURL || "").replace(/\/+$/, "")}/api/v1/projects/${encodeURIComponent(projectID)}/ctxpack`,
|
|
1887
|
+
synced_at: new Date().toISOString(),
|
|
1888
|
+
};
|
|
1857
1889
|
|
|
1858
1890
|
if (previousSig === remoteSig && hasAllCtxpackFiles(cacheDir, files)) {
|
|
1891
|
+
try {
|
|
1892
|
+
fs.writeFileSync(workspaceMetaPath, `${JSON.stringify(payload, null, 2)}\n`, "utf8");
|
|
1893
|
+
} catch {
|
|
1894
|
+
// Best-effort metadata refresh for workspace-root auto-detection.
|
|
1895
|
+
}
|
|
1859
1896
|
return {
|
|
1860
1897
|
sync_status: "current",
|
|
1861
1898
|
sync_message: "Local ctxpack cache is up to date.",
|
|
1862
1899
|
local_path: cacheDir,
|
|
1900
|
+
workspace_path: resolvedWorkspaceDir,
|
|
1863
1901
|
local_file_count: files.length,
|
|
1864
1902
|
};
|
|
1865
1903
|
}
|
|
@@ -1874,17 +1912,8 @@ function syncCtxpackToLocalCache({ siteBaseURL, projectID, ctxpack }) {
|
|
|
1874
1912
|
fs.writeFileSync(target, file.content, "utf8");
|
|
1875
1913
|
}
|
|
1876
1914
|
|
|
1877
|
-
const payload = {
|
|
1878
|
-
project_id: projectID,
|
|
1879
|
-
ctxpack_id: ctxpackID,
|
|
1880
|
-
version,
|
|
1881
|
-
status,
|
|
1882
|
-
files_count: files.length,
|
|
1883
|
-
files: files.map((f) => ({ path: f.path, doc_type: f.docType || "" })),
|
|
1884
|
-
source: `${String(siteBaseURL || "").replace(/\/+$/, "")}/api/v1/projects/${encodeURIComponent(projectID)}/ctxpack`,
|
|
1885
|
-
synced_at: new Date().toISOString(),
|
|
1886
|
-
};
|
|
1887
1915
|
fs.writeFileSync(metaPath, `${JSON.stringify(payload, null, 2)}\n`, "utf8");
|
|
1916
|
+
fs.writeFileSync(workspaceMetaPath, `${JSON.stringify(payload, null, 2)}\n`, "utf8");
|
|
1888
1917
|
|
|
1889
1918
|
return {
|
|
1890
1919
|
sync_status: previousMeta ? "updated" : "downloaded",
|
|
@@ -1892,6 +1921,7 @@ function syncCtxpackToLocalCache({ siteBaseURL, projectID, ctxpack }) {
|
|
|
1892
1921
|
? "Ctxpack updated to latest server version."
|
|
1893
1922
|
: "Ctxpack downloaded to local cache.",
|
|
1894
1923
|
local_path: cacheDir,
|
|
1924
|
+
workspace_path: resolvedWorkspaceDir,
|
|
1895
1925
|
local_file_count: files.length,
|
|
1896
1926
|
};
|
|
1897
1927
|
} catch (err) {
|
|
@@ -1899,6 +1929,7 @@ function syncCtxpackToLocalCache({ siteBaseURL, projectID, ctxpack }) {
|
|
|
1899
1929
|
sync_status: "error",
|
|
1900
1930
|
sync_message: String(err?.message || err),
|
|
1901
1931
|
local_path: cacheDir,
|
|
1932
|
+
workspace_path: resolvedWorkspaceDir,
|
|
1902
1933
|
local_file_count: 0,
|
|
1903
1934
|
};
|
|
1904
1935
|
}
|
|
@@ -1911,6 +1942,7 @@ async function loadProjectSummaryForTool({
|
|
|
1911
1942
|
timeoutSeconds,
|
|
1912
1943
|
includeCtxpack,
|
|
1913
1944
|
syncCtxpackLocal,
|
|
1945
|
+
workspaceDir,
|
|
1914
1946
|
}) {
|
|
1915
1947
|
const encodedProjectID = encodeURIComponent(projectID);
|
|
1916
1948
|
let projectRaw = null;
|
|
@@ -1968,6 +2000,7 @@ async function loadProjectSummaryForTool({
|
|
|
1968
2000
|
siteBaseURL,
|
|
1969
2001
|
projectID,
|
|
1970
2002
|
ctxpack,
|
|
2003
|
+
workspaceDir,
|
|
1971
2004
|
});
|
|
1972
2005
|
ctxpackSyncStatus = syncResult.sync_status || "error";
|
|
1973
2006
|
ctxpackSyncMessage = String(syncResult.sync_message || "").trim();
|
|
@@ -2008,6 +2041,7 @@ async function loadProjectSummaryForTool({
|
|
|
2008
2041
|
ctxpack_sync_message: ctxpackSyncMessage,
|
|
2009
2042
|
ctxpack_local_path: ctxpackLocalPath,
|
|
2010
2043
|
ctxpack_local_file_count: ctxpackLocalFileCount,
|
|
2044
|
+
ctxpack_workspace_path: String(workspaceDir || "").trim(),
|
|
2011
2045
|
agenda,
|
|
2012
2046
|
agenda_source: agendaSource,
|
|
2013
2047
|
};
|
|
@@ -2149,6 +2183,7 @@ async function appendWorkitemListHints(responseObj, args, toolArgs, token) {
|
|
|
2149
2183
|
timeoutSeconds: args.timeoutSeconds,
|
|
2150
2184
|
includeCtxpack: isEmptyBody,
|
|
2151
2185
|
syncCtxpackLocal: isEmptyBody,
|
|
2186
|
+
workspaceDir: args.workspaceDir,
|
|
2152
2187
|
});
|
|
2153
2188
|
if (String(summary.access || "") === "granted") {
|
|
2154
2189
|
nextLines.push("Project context:");
|
|
@@ -2186,12 +2221,66 @@ async function appendWorkitemListHints(responseObj, args, toolArgs, token) {
|
|
|
2186
2221
|
return responseObj;
|
|
2187
2222
|
}
|
|
2188
2223
|
|
|
2224
|
+
function appendCtxpackEnsureSyncHints(responseObj, args, toolArgs) {
|
|
2225
|
+
const result = safeObject(responseObj.result);
|
|
2226
|
+
const content = ensureArray(result.content);
|
|
2227
|
+
if (!content.length) return responseObj;
|
|
2228
|
+
|
|
2229
|
+
const first = safeObject(content[0]);
|
|
2230
|
+
if (String(first.type || "") !== "text") return responseObj;
|
|
2231
|
+
const text = String(first.text || "").trim();
|
|
2232
|
+
if (!text) return responseObj;
|
|
2233
|
+
|
|
2234
|
+
const envelope = parseToolEnvelopeFromRPCResult(result);
|
|
2235
|
+
if (!envelope) return responseObj;
|
|
2236
|
+
if (String(envelope.tool || "").trim() !== "ctxpack.ensure") return responseObj;
|
|
2237
|
+
if (!Boolean(envelope.ok) || Number(envelope.status || 0) !== 200) return responseObj;
|
|
2238
|
+
|
|
2239
|
+
const body = safeObject(envelope.body);
|
|
2240
|
+
if (!Object.keys(body).length) return responseObj;
|
|
2241
|
+
|
|
2242
|
+
const projectID = String(
|
|
2243
|
+
toolArgs?.project_id || toolArgs?.projectID || envelope.project_id || args.projectID || "",
|
|
2244
|
+
).trim();
|
|
2245
|
+
if (!projectID || !isUUID(projectID)) return responseObj;
|
|
2246
|
+
|
|
2247
|
+
const syncResult = syncCtxpackToLocalCache({
|
|
2248
|
+
siteBaseURL: normalizeSiteBaseURL(args.baseURL),
|
|
2249
|
+
projectID,
|
|
2250
|
+
ctxpack: body,
|
|
2251
|
+
workspaceDir: args.workspaceDir,
|
|
2252
|
+
});
|
|
2253
|
+
|
|
2254
|
+
const lines = [
|
|
2255
|
+
"",
|
|
2256
|
+
`ctxpack_local_sync_status: ${String(syncResult.sync_status || "error")}`,
|
|
2257
|
+
];
|
|
2258
|
+
if (syncResult.sync_message) {
|
|
2259
|
+
lines.push(`ctxpack_local_sync_message: ${String(syncResult.sync_message)}`);
|
|
2260
|
+
}
|
|
2261
|
+
if (syncResult.local_path) {
|
|
2262
|
+
lines.push(`ctxpack_local_path: ${String(syncResult.local_path)}`);
|
|
2263
|
+
}
|
|
2264
|
+
if (syncResult.workspace_path) {
|
|
2265
|
+
lines.push(`ctxpack_workspace_path: ${String(syncResult.workspace_path)}`);
|
|
2266
|
+
}
|
|
2267
|
+
|
|
2268
|
+
content[0] = {
|
|
2269
|
+
...first,
|
|
2270
|
+
text: `${text}\n${lines.join("\n")}`,
|
|
2271
|
+
};
|
|
2272
|
+
result.content = content;
|
|
2273
|
+
responseObj.result = result;
|
|
2274
|
+
return responseObj;
|
|
2275
|
+
}
|
|
2276
|
+
|
|
2189
2277
|
async function runProxy(flags) {
|
|
2190
2278
|
const workspaceMeta = loadWorkspaceMeta(process.cwd());
|
|
2191
2279
|
const args = {
|
|
2192
2280
|
baseURL: flags["base-url"] || DEFAULT_BASE_URL,
|
|
2193
2281
|
projectID: String(flags["project-id"] || workspaceMeta.project_id || "").trim(),
|
|
2194
2282
|
ctxpackKey: String(flags["ctxpack-key"] || buildCtxpackKeyFromMeta(workspaceMeta) || "").trim(),
|
|
2283
|
+
workspaceDir: resolveWorkspaceDir(flags["workspace-dir"] || process.cwd()),
|
|
2195
2284
|
includeDrafts: boolFromRaw(flags["include-drafts"], true),
|
|
2196
2285
|
timeoutSeconds: intFromRaw(flags["timeout-seconds"], 30),
|
|
2197
2286
|
};
|
|
@@ -2289,6 +2378,7 @@ async function runProxy(flags) {
|
|
|
2289
2378
|
timeoutSeconds: args.timeoutSeconds,
|
|
2290
2379
|
includeCtxpack,
|
|
2291
2380
|
syncCtxpackLocal,
|
|
2381
|
+
workspaceDir: args.workspaceDir,
|
|
2292
2382
|
});
|
|
2293
2383
|
const text = buildProjectSummaryText(summary);
|
|
2294
2384
|
process.stdout.write(
|
|
@@ -2324,6 +2414,8 @@ async function runProxy(flags) {
|
|
|
2324
2414
|
patched = appendProjectHintToInitialize(patched, args);
|
|
2325
2415
|
} else if (isJsonRpcMethod(requestObj, "tools/call") && toolName === "workitem.list") {
|
|
2326
2416
|
patched = await appendWorkitemListHints(patched, args, toolArgs, token);
|
|
2417
|
+
} else if (isJsonRpcMethod(requestObj, "tools/call") && toolName === "ctxpack.ensure") {
|
|
2418
|
+
patched = appendCtxpackEnsureSyncHints(patched, args, toolArgs);
|
|
2327
2419
|
}
|
|
2328
2420
|
process.stdout.write(`${JSON.stringify(patched)}\n`);
|
|
2329
2421
|
return;
|
|
@@ -2408,11 +2500,27 @@ function resolveSetupContext(flags) {
|
|
|
2408
2500
|
const projectID = String(flags["project-id"] || workspaceMeta.project_id || "").trim();
|
|
2409
2501
|
const ctxpackKey = String(flags["ctxpack-key"] || buildCtxpackKeyFromMeta(workspaceMeta) || "").trim();
|
|
2410
2502
|
const baseURL = String(flags["base-url"] || DEFAULT_SITE_URL).trim().replace(/\/+$/, "");
|
|
2503
|
+
const workspaceDirRaw = String(flags["workspace-dir"] || "").trim();
|
|
2504
|
+
const hasExplicitWorkspaceDir = workspaceDirRaw.length > 0;
|
|
2505
|
+
const workspaceDir = resolveWorkspaceDir(hasExplicitWorkspaceDir ? workspaceDirRaw : process.cwd());
|
|
2411
2506
|
const serverName = String(flags.name || DEFAULT_SERVER_NAME).trim() || DEFAULT_SERVER_NAME;
|
|
2412
2507
|
const proxyArgs = ["--base-url", `${baseURL}/governance/mcp`];
|
|
2508
|
+
// Default mode: do not pin workspace path in MCP registration.
|
|
2509
|
+
// Let client runtime cwd resolve per open folder/workspace.
|
|
2510
|
+
if (hasExplicitWorkspaceDir) {
|
|
2511
|
+
proxyArgs.push("--workspace-dir", workspaceDir);
|
|
2512
|
+
}
|
|
2413
2513
|
if (projectID) proxyArgs.push("--project-id", projectID);
|
|
2414
2514
|
if (ctxpackKey) proxyArgs.push("--ctxpack-key", ctxpackKey);
|
|
2415
|
-
return {
|
|
2515
|
+
return {
|
|
2516
|
+
projectID,
|
|
2517
|
+
ctxpackKey,
|
|
2518
|
+
baseURL,
|
|
2519
|
+
workspaceDir,
|
|
2520
|
+
hasExplicitWorkspaceDir,
|
|
2521
|
+
serverName,
|
|
2522
|
+
proxyArgs,
|
|
2523
|
+
};
|
|
2416
2524
|
}
|
|
2417
2525
|
|
|
2418
2526
|
function runSetupInternal(flags, options = {}) {
|
|
@@ -2424,20 +2532,26 @@ function runSetupInternal(flags, options = {}) {
|
|
|
2424
2532
|
for (const cliBin of clients) {
|
|
2425
2533
|
if (!commandExists(cliBin)) continue;
|
|
2426
2534
|
const alreadyRegistered = isRegistered(cliBin, context.serverName);
|
|
2427
|
-
if (ensureOnly
|
|
2428
|
-
results.push({ cliBin, ok: true, action: "already-registered" });
|
|
2429
|
-
continue;
|
|
2430
|
-
}
|
|
2431
|
-
if (!ensureOnly) {
|
|
2535
|
+
if (!ensureOnly || alreadyRegistered) {
|
|
2432
2536
|
runRemove(cliBin, context.serverName);
|
|
2433
2537
|
}
|
|
2434
2538
|
const ok = tryRegister(cliBin, context.serverName, context.proxyArgs);
|
|
2435
|
-
|
|
2539
|
+
const action = ensureOnly
|
|
2540
|
+
? alreadyRegistered
|
|
2541
|
+
? "updated"
|
|
2542
|
+
: "registered"
|
|
2543
|
+
: alreadyRegistered
|
|
2544
|
+
? "re-registered"
|
|
2545
|
+
: "registered";
|
|
2546
|
+
results.push({ cliBin, ok, action });
|
|
2436
2547
|
}
|
|
2437
2548
|
|
|
2438
2549
|
process.stdout.write(ensureOnly ? "\nEnsure complete.\n" : "\nInstall complete.\n");
|
|
2439
2550
|
process.stdout.write(`Server: ${context.serverName}\n`);
|
|
2440
2551
|
process.stdout.write(`Gateway: ${context.baseURL}/governance/mcp\n`);
|
|
2552
|
+
process.stdout.write(
|
|
2553
|
+
`Workspace: ${context.hasExplicitWorkspaceDir ? context.workspaceDir : "auto (client current folder)"}\n`,
|
|
2554
|
+
);
|
|
2441
2555
|
process.stdout.write(`Project: ${context.projectID || "auto-detect from .metheus_ctxpack_sync.json"}\n`);
|
|
2442
2556
|
if (context.ctxpackKey) {
|
|
2443
2557
|
process.stdout.write(`Ctxpack: ${context.ctxpackKey}\n`);
|