@great-arrow/cli 0.1.2 → 0.1.4
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/dist/index.js +661 -238
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -14,11 +14,7 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
|
|
|
14
14
|
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
15
15
|
});
|
|
16
16
|
var __commonJS = (cb, mod) => function __require2() {
|
|
17
|
-
|
|
18
|
-
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
19
|
-
} catch (e) {
|
|
20
|
-
throw mod = 0, e;
|
|
21
|
-
}
|
|
17
|
+
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
22
18
|
};
|
|
23
19
|
var __copyProps = (to, from, except, desc) => {
|
|
24
20
|
if (from && typeof from === "object" || typeof from === "function") {
|
|
@@ -37,9 +33,9 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
37
33
|
mod
|
|
38
34
|
));
|
|
39
35
|
|
|
40
|
-
// node_modules/commander/lib/error.js
|
|
36
|
+
// node_modules/.pnpm/commander@13.1.0/node_modules/commander/lib/error.js
|
|
41
37
|
var require_error = __commonJS({
|
|
42
|
-
"node_modules/commander/lib/error.js"(exports) {
|
|
38
|
+
"node_modules/.pnpm/commander@13.1.0/node_modules/commander/lib/error.js"(exports) {
|
|
43
39
|
var CommanderError2 = class extends Error {
|
|
44
40
|
/**
|
|
45
41
|
* Constructs the CommanderError class
|
|
@@ -72,9 +68,9 @@ var require_error = __commonJS({
|
|
|
72
68
|
}
|
|
73
69
|
});
|
|
74
70
|
|
|
75
|
-
// node_modules/commander/lib/argument.js
|
|
71
|
+
// node_modules/.pnpm/commander@13.1.0/node_modules/commander/lib/argument.js
|
|
76
72
|
var require_argument = __commonJS({
|
|
77
|
-
"node_modules/commander/lib/argument.js"(exports) {
|
|
73
|
+
"node_modules/.pnpm/commander@13.1.0/node_modules/commander/lib/argument.js"(exports) {
|
|
78
74
|
var { InvalidArgumentError: InvalidArgumentError2 } = require_error();
|
|
79
75
|
var Argument2 = class {
|
|
80
76
|
/**
|
|
@@ -199,9 +195,9 @@ var require_argument = __commonJS({
|
|
|
199
195
|
}
|
|
200
196
|
});
|
|
201
197
|
|
|
202
|
-
// node_modules/commander/lib/help.js
|
|
198
|
+
// node_modules/.pnpm/commander@13.1.0/node_modules/commander/lib/help.js
|
|
203
199
|
var require_help = __commonJS({
|
|
204
|
-
"node_modules/commander/lib/help.js"(exports) {
|
|
200
|
+
"node_modules/.pnpm/commander@13.1.0/node_modules/commander/lib/help.js"(exports) {
|
|
205
201
|
var { humanReadableArgName } = require_argument();
|
|
206
202
|
var Help2 = class {
|
|
207
203
|
constructor() {
|
|
@@ -768,9 +764,9 @@ ${itemIndentStr}`);
|
|
|
768
764
|
}
|
|
769
765
|
});
|
|
770
766
|
|
|
771
|
-
// node_modules/commander/lib/option.js
|
|
767
|
+
// node_modules/.pnpm/commander@13.1.0/node_modules/commander/lib/option.js
|
|
772
768
|
var require_option = __commonJS({
|
|
773
|
-
"node_modules/commander/lib/option.js"(exports) {
|
|
769
|
+
"node_modules/.pnpm/commander@13.1.0/node_modules/commander/lib/option.js"(exports) {
|
|
774
770
|
var { InvalidArgumentError: InvalidArgumentError2 } = require_error();
|
|
775
771
|
var Option2 = class {
|
|
776
772
|
/**
|
|
@@ -1069,9 +1065,9 @@ var require_option = __commonJS({
|
|
|
1069
1065
|
}
|
|
1070
1066
|
});
|
|
1071
1067
|
|
|
1072
|
-
// node_modules/commander/lib/suggestSimilar.js
|
|
1068
|
+
// node_modules/.pnpm/commander@13.1.0/node_modules/commander/lib/suggestSimilar.js
|
|
1073
1069
|
var require_suggestSimilar = __commonJS({
|
|
1074
|
-
"node_modules/commander/lib/suggestSimilar.js"(exports) {
|
|
1070
|
+
"node_modules/.pnpm/commander@13.1.0/node_modules/commander/lib/suggestSimilar.js"(exports) {
|
|
1075
1071
|
var maxDistance = 3;
|
|
1076
1072
|
function editDistance(a, b) {
|
|
1077
1073
|
if (Math.abs(a.length - b.length) > maxDistance)
|
|
@@ -1149,9 +1145,9 @@ var require_suggestSimilar = __commonJS({
|
|
|
1149
1145
|
}
|
|
1150
1146
|
});
|
|
1151
1147
|
|
|
1152
|
-
// node_modules/commander/lib/command.js
|
|
1148
|
+
// node_modules/.pnpm/commander@13.1.0/node_modules/commander/lib/command.js
|
|
1153
1149
|
var require_command = __commonJS({
|
|
1154
|
-
"node_modules/commander/lib/command.js"(exports) {
|
|
1150
|
+
"node_modules/.pnpm/commander@13.1.0/node_modules/commander/lib/command.js"(exports) {
|
|
1155
1151
|
var EventEmitter = __require("node:events").EventEmitter;
|
|
1156
1152
|
var childProcess = __require("node:child_process");
|
|
1157
1153
|
var path = __require("node:path");
|
|
@@ -3308,9 +3304,9 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
3308
3304
|
}
|
|
3309
3305
|
});
|
|
3310
3306
|
|
|
3311
|
-
// node_modules/commander/index.js
|
|
3307
|
+
// node_modules/.pnpm/commander@13.1.0/node_modules/commander/index.js
|
|
3312
3308
|
var require_commander = __commonJS({
|
|
3313
|
-
"node_modules/commander/index.js"(exports) {
|
|
3309
|
+
"node_modules/.pnpm/commander@13.1.0/node_modules/commander/index.js"(exports) {
|
|
3314
3310
|
var { Argument: Argument2 } = require_argument();
|
|
3315
3311
|
var { Command: Command2 } = require_command();
|
|
3316
3312
|
var { CommanderError: CommanderError2, InvalidArgumentError: InvalidArgumentError2 } = require_error();
|
|
@@ -3330,7 +3326,7 @@ var require_commander = __commonJS({
|
|
|
3330
3326
|
}
|
|
3331
3327
|
});
|
|
3332
3328
|
|
|
3333
|
-
// node_modules/commander/esm.mjs
|
|
3329
|
+
// node_modules/.pnpm/commander@13.1.0/node_modules/commander/esm.mjs
|
|
3334
3330
|
var import_index = __toESM(require_commander(), 1);
|
|
3335
3331
|
var {
|
|
3336
3332
|
program,
|
|
@@ -3862,6 +3858,61 @@ async function resolveToken(configToken) {
|
|
|
3862
3858
|
|
|
3863
3859
|
// src/lib/client.ts
|
|
3864
3860
|
import { execSync } from "node:child_process";
|
|
3861
|
+
|
|
3862
|
+
// src/lib/mcp-payload.ts
|
|
3863
|
+
function isTextBlock(value) {
|
|
3864
|
+
return typeof value === "object" && value !== null && value.type === "text" && typeof value.text === "string";
|
|
3865
|
+
}
|
|
3866
|
+
function extractJsonSlice(text) {
|
|
3867
|
+
const start = text.search(/[{[]/);
|
|
3868
|
+
if (start === -1) return null;
|
|
3869
|
+
let depth = 0;
|
|
3870
|
+
let inString = false;
|
|
3871
|
+
let escaped = false;
|
|
3872
|
+
for (let i = start; i < text.length; i++) {
|
|
3873
|
+
const ch = text[i];
|
|
3874
|
+
if (escaped) {
|
|
3875
|
+
escaped = false;
|
|
3876
|
+
continue;
|
|
3877
|
+
}
|
|
3878
|
+
if (inString) {
|
|
3879
|
+
if (ch === "\\") escaped = true;
|
|
3880
|
+
else if (ch === '"') inString = false;
|
|
3881
|
+
continue;
|
|
3882
|
+
}
|
|
3883
|
+
if (ch === '"') inString = true;
|
|
3884
|
+
else if (ch === "{" || ch === "[") depth++;
|
|
3885
|
+
else if (ch === "}" || ch === "]") {
|
|
3886
|
+
depth--;
|
|
3887
|
+
if (depth === 0) return text.slice(start, i + 1);
|
|
3888
|
+
}
|
|
3889
|
+
}
|
|
3890
|
+
return null;
|
|
3891
|
+
}
|
|
3892
|
+
function parseMcpPayload(result) {
|
|
3893
|
+
let text = null;
|
|
3894
|
+
if (typeof result === "string") {
|
|
3895
|
+
text = result;
|
|
3896
|
+
} else if (Array.isArray(result)) {
|
|
3897
|
+
const block = result.find(isTextBlock);
|
|
3898
|
+
if (block) text = block.text;
|
|
3899
|
+
}
|
|
3900
|
+
if (text === null) return result;
|
|
3901
|
+
try {
|
|
3902
|
+
return JSON.parse(text);
|
|
3903
|
+
} catch {
|
|
3904
|
+
}
|
|
3905
|
+
const slice = extractJsonSlice(text);
|
|
3906
|
+
if (slice !== null) {
|
|
3907
|
+
try {
|
|
3908
|
+
return JSON.parse(slice);
|
|
3909
|
+
} catch {
|
|
3910
|
+
}
|
|
3911
|
+
}
|
|
3912
|
+
return text;
|
|
3913
|
+
}
|
|
3914
|
+
|
|
3915
|
+
// src/lib/client.ts
|
|
3865
3916
|
var DEFAULT_TIMEOUT_MS = 3e4;
|
|
3866
3917
|
var MAX_RETRIES = 3;
|
|
3867
3918
|
var BASE_DELAY_MS = 1e3;
|
|
@@ -3995,7 +4046,7 @@ var GadClient = class {
|
|
|
3995
4046
|
* @param toolName - The MCP tool name to invoke
|
|
3996
4047
|
* @param args - Arguments to pass to the tool
|
|
3997
4048
|
* @param timeoutMs - Optional timeout override (e.g., 90s for agent calls)
|
|
3998
|
-
* @returns The tool result content
|
|
4049
|
+
* @returns The decoded JSON payload from the tool result content
|
|
3999
4050
|
*/
|
|
4000
4051
|
async mcpCall(toolName, args, timeoutMs) {
|
|
4001
4052
|
const url = `${this.apiUrl}/api/mcp`;
|
|
@@ -4035,7 +4086,15 @@ var GadClient = class {
|
|
|
4035
4086
|
data.error.data ?? void 0
|
|
4036
4087
|
);
|
|
4037
4088
|
}
|
|
4038
|
-
|
|
4089
|
+
const payload = parseMcpPayload(data.result?.content ?? data.result);
|
|
4090
|
+
if (typeof payload === "string" && payload.includes("[truncated: result exceeded")) {
|
|
4091
|
+
throw new CliError(
|
|
4092
|
+
`Tool '${toolName}' response was truncated by the server before the data completed.`,
|
|
4093
|
+
3 /* VALIDATION_ERROR */,
|
|
4094
|
+
"Narrow the request (lower --limit / -k, or add filters) and try again."
|
|
4095
|
+
);
|
|
4096
|
+
}
|
|
4097
|
+
return payload;
|
|
4039
4098
|
}
|
|
4040
4099
|
/**
|
|
4041
4100
|
* Make a GET request to a REST API endpoint.
|
|
@@ -4094,6 +4153,18 @@ var GadClient = class {
|
|
|
4094
4153
|
}
|
|
4095
4154
|
};
|
|
4096
4155
|
|
|
4156
|
+
// src/lib/global-opts.ts
|
|
4157
|
+
function globalOutputOpts(cmd) {
|
|
4158
|
+
const merged = cmd.optsWithGlobals();
|
|
4159
|
+
return {
|
|
4160
|
+
json: Boolean(merged.json),
|
|
4161
|
+
quiet: Boolean(merged.quiet),
|
|
4162
|
+
// --no-color sets `color: false`; some commands declare `--no-color`
|
|
4163
|
+
// locally which surfaces as `noColor`.
|
|
4164
|
+
noColor: merged.color === false || Boolean(merged.noColor)
|
|
4165
|
+
};
|
|
4166
|
+
}
|
|
4167
|
+
|
|
4097
4168
|
// src/commands/whoami.ts
|
|
4098
4169
|
function maskToken2(token) {
|
|
4099
4170
|
if (token.length <= 8) {
|
|
@@ -4108,7 +4179,8 @@ function extractWorkspaceName(data) {
|
|
|
4108
4179
|
return data.workspace?.name || data.workspaceName || "unknown";
|
|
4109
4180
|
}
|
|
4110
4181
|
function whoamiCommand() {
|
|
4111
|
-
const cmd = new Command("whoami").description("Display authenticated user info").option("--json", "Output as JSON").option("--quiet", "Suppress decorative output").option("--no-color", "Disable color output").action(async (options) => {
|
|
4182
|
+
const cmd = new Command("whoami").description("Display authenticated user info").option("--json", "Output as JSON").option("--quiet", "Suppress decorative output").option("--no-color", "Disable color output").action(async (options, command) => {
|
|
4183
|
+
const g = globalOutputOpts(command);
|
|
4112
4184
|
const auth = await resolveAuth();
|
|
4113
4185
|
const client = new GadClient({
|
|
4114
4186
|
token: auth.token,
|
|
@@ -4131,10 +4203,10 @@ function whoamiCommand() {
|
|
|
4131
4203
|
const workspaceName = extractWorkspaceName(introspect);
|
|
4132
4204
|
const tokenPrefix = maskToken2(auth.token);
|
|
4133
4205
|
const fmt = createFormatter({
|
|
4134
|
-
json:
|
|
4135
|
-
quiet:
|
|
4206
|
+
json: g.json,
|
|
4207
|
+
quiet: g.quiet,
|
|
4136
4208
|
isTTY: isTTY(),
|
|
4137
|
-
noColor:
|
|
4209
|
+
noColor: g.noColor
|
|
4138
4210
|
});
|
|
4139
4211
|
const data = {
|
|
4140
4212
|
email,
|
|
@@ -4225,7 +4297,7 @@ var memoryCommand = new Command("memory").description(
|
|
|
4225
4297
|
memoryCommand.command("create").description("Create a new memory in the active workspace").requiredOption("--title <title>", "Memory title").option("--content <content>", "Memory content (or pipe via stdin)").option("--tags <tags>", "Comma-separated tags").option(
|
|
4226
4298
|
"--layer <layer>",
|
|
4227
4299
|
"Memory layer (WORKING, EPISODIC, SEMANTIC, PROCEDURAL)"
|
|
4228
|
-
).action(async (opts) => {
|
|
4300
|
+
).action(async (opts, command) => {
|
|
4229
4301
|
const client = await buildClient();
|
|
4230
4302
|
let content = opts.content;
|
|
4231
4303
|
if (!content) {
|
|
@@ -4262,7 +4334,8 @@ memoryCommand.command("create").description("Create a new memory in the active w
|
|
|
4262
4334
|
const memoryId = typeof rawId === "string" ? rawId : "created";
|
|
4263
4335
|
fmt.success(`Memory created: ${memoryId}`);
|
|
4264
4336
|
});
|
|
4265
|
-
memoryCommand.command("search <query>").description("Semantic search across memories").option("--json", "Output as JSON").option("-k, --limit <number>", "Max results", "10").option("--layer <layer>", "Filter by layer").action(async (query, opts) => {
|
|
4337
|
+
memoryCommand.command("search <query>").description("Semantic search across memories").option("--json", "Output as JSON").option("-k, --limit <number>", "Max results", "10").option("--layer <layer>", "Filter by layer").action(async (query, opts, command) => {
|
|
4338
|
+
const g = globalOutputOpts(command);
|
|
4266
4339
|
const client = await buildClient();
|
|
4267
4340
|
const args = {
|
|
4268
4341
|
query,
|
|
@@ -4272,22 +4345,27 @@ memoryCommand.command("search <query>").description("Semantic search across memo
|
|
|
4272
4345
|
args.layer = opts.layer;
|
|
4273
4346
|
}
|
|
4274
4347
|
const result = await client.mcpCall("memory_search", args);
|
|
4275
|
-
const
|
|
4348
|
+
const data = result ?? {};
|
|
4349
|
+
const rawResults = Array.isArray(result) ? result : data.hits ?? data.results ?? [];
|
|
4276
4350
|
const rows = rawResults.map((item) => ({
|
|
4277
4351
|
...item,
|
|
4352
|
+
title: item.title ?? item.memoryTitle,
|
|
4278
4353
|
preview: extractPreview(item)
|
|
4279
4354
|
}));
|
|
4280
4355
|
const fmt = createFormatter({
|
|
4281
|
-
json:
|
|
4356
|
+
json: g.json,
|
|
4282
4357
|
quiet: false,
|
|
4283
4358
|
isTTY: isTTY(),
|
|
4284
4359
|
noColor: false
|
|
4285
4360
|
});
|
|
4286
4361
|
fmt.table(rows, searchColumns);
|
|
4287
4362
|
});
|
|
4288
|
-
memoryCommand.command("list").description("List recent memories with optional filters").option("--json", "Output as JSON").option("--layer <layer>", "Filter by memory layer").option("--tag <tag>", "Filter by tag").action(async (opts) => {
|
|
4363
|
+
memoryCommand.command("list").description("List recent memories with optional filters").option("--json", "Output as JSON").option("--limit <number>", "Max results", "20").option("--layer <layer>", "Filter by memory layer").option("--tag <tag>", "Filter by tag").action(async (opts, command) => {
|
|
4364
|
+
const g = globalOutputOpts(command);
|
|
4289
4365
|
const client = await buildClient();
|
|
4290
|
-
const args = {
|
|
4366
|
+
const args = {
|
|
4367
|
+
limit: Number.parseInt(opts.limit, 10)
|
|
4368
|
+
};
|
|
4291
4369
|
if (opts.layer) {
|
|
4292
4370
|
args.layer = opts.layer;
|
|
4293
4371
|
}
|
|
@@ -4295,21 +4373,23 @@ memoryCommand.command("list").description("List recent memories with optional fi
|
|
|
4295
4373
|
args.tag = opts.tag;
|
|
4296
4374
|
}
|
|
4297
4375
|
const result = await client.mcpCall("memory_list", args);
|
|
4298
|
-
const
|
|
4376
|
+
const data = result ?? {};
|
|
4377
|
+
const rawResults = Array.isArray(result) ? result : data.memories ?? data.results ?? [];
|
|
4299
4378
|
const fmt = createFormatter({
|
|
4300
|
-
json:
|
|
4379
|
+
json: g.json,
|
|
4301
4380
|
quiet: false,
|
|
4302
4381
|
isTTY: isTTY(),
|
|
4303
4382
|
noColor: false
|
|
4304
4383
|
});
|
|
4305
4384
|
fmt.table(rawResults, listColumns);
|
|
4306
4385
|
});
|
|
4307
|
-
memoryCommand.command("get <id>").description("Show full content and metadata of a memory").option("--json", "Output as JSON").action(async (id, opts) => {
|
|
4386
|
+
memoryCommand.command("get <id>").description("Show full content and metadata of a memory").option("--json", "Output as JSON").action(async (id, opts, command) => {
|
|
4387
|
+
const g = globalOutputOpts(command);
|
|
4308
4388
|
const client = await buildClient();
|
|
4309
4389
|
const result = await client.mcpCall("memory_get", { id });
|
|
4310
4390
|
const data = result ?? {};
|
|
4311
4391
|
const fmt = createFormatter({
|
|
4312
|
-
json:
|
|
4392
|
+
json: g.json,
|
|
4313
4393
|
quiet: false,
|
|
4314
4394
|
isTTY: isTTY(),
|
|
4315
4395
|
noColor: false
|
|
@@ -4380,22 +4460,10 @@ async function createClient() {
|
|
|
4380
4460
|
const auth = await resolveAuth();
|
|
4381
4461
|
return new GadClient({ token: auth.token, apiUrl: auth.apiUrl });
|
|
4382
4462
|
}
|
|
4383
|
-
function extractMcpContent(result) {
|
|
4384
|
-
if (Array.isArray(result) && result.length > 0) {
|
|
4385
|
-
const first = result[0];
|
|
4386
|
-
if (typeof first === "object" && first !== null && "type" in first && first.type === "text" && "text" in first) {
|
|
4387
|
-
try {
|
|
4388
|
-
return JSON.parse(first.text);
|
|
4389
|
-
} catch {
|
|
4390
|
-
return first.text;
|
|
4391
|
-
}
|
|
4392
|
-
}
|
|
4393
|
-
}
|
|
4394
|
-
return result;
|
|
4395
|
-
}
|
|
4396
4463
|
var listCommand = new Command("list").description("List all accessible workspaces").option("--json", "Output as JSON").action(async (options) => {
|
|
4397
|
-
const
|
|
4398
|
-
|
|
4464
|
+
const jsonFlag = Boolean(
|
|
4465
|
+
options.json || listCommand.optsWithGlobals().json
|
|
4466
|
+
);
|
|
4399
4467
|
const fmt = createFormatter({
|
|
4400
4468
|
json: jsonFlag,
|
|
4401
4469
|
quiet: false,
|
|
@@ -4404,7 +4472,7 @@ var listCommand = new Command("list").description("List all accessible workspace
|
|
|
4404
4472
|
});
|
|
4405
4473
|
const client = await createClient();
|
|
4406
4474
|
const result = await client.mcpCall("workspace_list", {});
|
|
4407
|
-
const data =
|
|
4475
|
+
const data = result;
|
|
4408
4476
|
let workspaces = [];
|
|
4409
4477
|
if (Array.isArray(data)) {
|
|
4410
4478
|
workspaces = data;
|
|
@@ -4466,8 +4534,9 @@ var infoColumns = [
|
|
|
4466
4534
|
}
|
|
4467
4535
|
];
|
|
4468
4536
|
var infoCommand = new Command("info").description("Show active workspace settings, member count, and integrations").option("--json", "Output as JSON").action(async (options) => {
|
|
4469
|
-
const
|
|
4470
|
-
|
|
4537
|
+
const jsonFlag = Boolean(
|
|
4538
|
+
options.json || infoCommand.optsWithGlobals().json
|
|
4539
|
+
);
|
|
4471
4540
|
const fmt = createFormatter({
|
|
4472
4541
|
json: jsonFlag,
|
|
4473
4542
|
quiet: false,
|
|
@@ -4476,13 +4545,14 @@ var infoCommand = new Command("info").description("Show active workspace setting
|
|
|
4476
4545
|
});
|
|
4477
4546
|
const client = await createClient();
|
|
4478
4547
|
const result = await client.mcpCall("workspace_settings_get", {});
|
|
4479
|
-
const data =
|
|
4548
|
+
const data = result;
|
|
4480
4549
|
const workspace = typeof data === "object" && data !== null ? data : {};
|
|
4481
4550
|
fmt.detail(workspace, infoColumns);
|
|
4482
4551
|
});
|
|
4483
4552
|
var membersCommand = new Command("members").description("List workspace members with roles and last-active timestamps").option("--json", "Output as JSON").action(async (options) => {
|
|
4484
|
-
const
|
|
4485
|
-
|
|
4553
|
+
const jsonFlag = Boolean(
|
|
4554
|
+
options.json || membersCommand.optsWithGlobals().json
|
|
4555
|
+
);
|
|
4486
4556
|
const fmt = createFormatter({
|
|
4487
4557
|
json: jsonFlag,
|
|
4488
4558
|
quiet: false,
|
|
@@ -4491,7 +4561,7 @@ var membersCommand = new Command("members").description("List workspace members
|
|
|
4491
4561
|
});
|
|
4492
4562
|
const client = await createClient();
|
|
4493
4563
|
const result = await client.mcpCall("workspace_members", {});
|
|
4494
|
-
const data =
|
|
4564
|
+
const data = result;
|
|
4495
4565
|
let members = [];
|
|
4496
4566
|
if (Array.isArray(data)) {
|
|
4497
4567
|
members = data;
|
|
@@ -4544,7 +4614,7 @@ var listCommand2 = new Command("list").description("List open todos sorted by pr
|
|
|
4544
4614
|
"Filter by status (open, in_progress, review, done, dismissed)"
|
|
4545
4615
|
).option("--json", "Output as JSON").action(
|
|
4546
4616
|
async (options, cmd) => {
|
|
4547
|
-
const parentOpts = cmd.
|
|
4617
|
+
const parentOpts = cmd.optsWithGlobals();
|
|
4548
4618
|
const jsonOutput = options.json || parentOpts.json || false;
|
|
4549
4619
|
const fmt = createFormatter({
|
|
4550
4620
|
json: jsonOutput,
|
|
@@ -4554,10 +4624,9 @@ var listCommand2 = new Command("list").description("List open todos sorted by pr
|
|
|
4554
4624
|
});
|
|
4555
4625
|
const auth = await resolveAuth();
|
|
4556
4626
|
const client = new GadClient({ token: auth.token, apiUrl: auth.apiUrl });
|
|
4557
|
-
const args = {
|
|
4558
|
-
|
|
4559
|
-
|
|
4560
|
-
}
|
|
4627
|
+
const args = {
|
|
4628
|
+
status: options.status ?? "open"
|
|
4629
|
+
};
|
|
4561
4630
|
const result = await client.mcpCall("todo_list", args);
|
|
4562
4631
|
let todos = [];
|
|
4563
4632
|
if (Array.isArray(result)) {
|
|
@@ -4634,6 +4703,271 @@ var updateCommand = new Command("update").description("Update a todo's status").
|
|
|
4634
4703
|
var todoCommand = new Command("todo").description("Manage kanban todos").addCommand(listCommand2).addCommand(createCommand2).addCommand(doneCommand).addCommand(updateCommand);
|
|
4635
4704
|
var todo_default = todoCommand;
|
|
4636
4705
|
|
|
4706
|
+
// src/commands/doctor.ts
|
|
4707
|
+
import { existsSync } from "node:fs";
|
|
4708
|
+
import { copyFile, mkdir as mkdir2, readFile as readFile2, writeFile as writeFile2 } from "node:fs/promises";
|
|
4709
|
+
import { homedir as homedir2 } from "node:os";
|
|
4710
|
+
import { dirname as dirname2, join as join2 } from "node:path";
|
|
4711
|
+
|
|
4712
|
+
// src/lib/codex-config.ts
|
|
4713
|
+
import { execFileSync } from "node:child_process";
|
|
4714
|
+
var CODEX_SERVER_NAME = "great-arrow";
|
|
4715
|
+
var CODEX_CLIENT_ID = "codex-cli";
|
|
4716
|
+
var SAFE_GLOBAL_MCP_SERVERS = /* @__PURE__ */ new Set([
|
|
4717
|
+
CODEX_SERVER_NAME,
|
|
4718
|
+
"great-arrow-kiro-terminal",
|
|
4719
|
+
"chrome-devtools",
|
|
4720
|
+
"node_repl",
|
|
4721
|
+
"playwright",
|
|
4722
|
+
"postman",
|
|
4723
|
+
"stripe",
|
|
4724
|
+
"vercel",
|
|
4725
|
+
"xcodebuildmcp"
|
|
4726
|
+
]);
|
|
4727
|
+
function normalizeApiUrl(apiUrl) {
|
|
4728
|
+
return apiUrl.replace(/\/+$/, "");
|
|
4729
|
+
}
|
|
4730
|
+
function mcpEndpoint(apiUrl) {
|
|
4731
|
+
return `${normalizeApiUrl(apiUrl)}/api/mcp`;
|
|
4732
|
+
}
|
|
4733
|
+
function tomlEscape(value) {
|
|
4734
|
+
return value.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
|
|
4735
|
+
}
|
|
4736
|
+
function detectGitOrigin2(cwd = process.cwd()) {
|
|
4737
|
+
try {
|
|
4738
|
+
const env = { ...process.env };
|
|
4739
|
+
for (const key of Object.keys(env)) {
|
|
4740
|
+
if (key.startsWith("GIT_")) delete env[key];
|
|
4741
|
+
}
|
|
4742
|
+
const url = execFileSync(
|
|
4743
|
+
"git",
|
|
4744
|
+
["-C", cwd, "remote", "get-url", "origin"],
|
|
4745
|
+
{
|
|
4746
|
+
encoding: "utf8",
|
|
4747
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
4748
|
+
env,
|
|
4749
|
+
timeout: 1e3
|
|
4750
|
+
}
|
|
4751
|
+
).trim();
|
|
4752
|
+
return url || null;
|
|
4753
|
+
} catch {
|
|
4754
|
+
return null;
|
|
4755
|
+
}
|
|
4756
|
+
}
|
|
4757
|
+
function repoKey(repoUrl) {
|
|
4758
|
+
if (!repoUrl) return null;
|
|
4759
|
+
const trimmed = repoUrl.trim();
|
|
4760
|
+
if (!trimmed) return null;
|
|
4761
|
+
const scpLike = trimmed.match(/^git@([^:]+):(.+)$/);
|
|
4762
|
+
const asUrl = scpLike ? `https://${scpLike[1]}/${scpLike[2]}` : trimmed.replace(/^ssh:\/\/git@/i, "https://");
|
|
4763
|
+
try {
|
|
4764
|
+
const parsed = new URL(asUrl);
|
|
4765
|
+
const host = parsed.hostname.toLowerCase();
|
|
4766
|
+
let path = parsed.pathname.replace(/^\/+/, "").replace(/\/+$/, "");
|
|
4767
|
+
path = path.replace(/\.git$/i, "").toLowerCase();
|
|
4768
|
+
return path ? `${host}/${path}` : null;
|
|
4769
|
+
} catch {
|
|
4770
|
+
return null;
|
|
4771
|
+
}
|
|
4772
|
+
}
|
|
4773
|
+
function generateCodexConfigToml(apiUrl, repoUrl) {
|
|
4774
|
+
const repoHeader = repoUrl ? `X-Repo-URL = "${tomlEscape(repoUrl)}"
|
|
4775
|
+
` : "";
|
|
4776
|
+
return `[mcp_servers.${CODEX_SERVER_NAME}]
|
|
4777
|
+
transport = "streamable_http"
|
|
4778
|
+
url = "${tomlEscape(mcpEndpoint(apiUrl))}"
|
|
4779
|
+
bearer_token_env_var = "GREAT_ARROW_TOKEN"
|
|
4780
|
+
|
|
4781
|
+
[mcp_servers.${CODEX_SERVER_NAME}.http_headers]
|
|
4782
|
+
X-MCP-Client = "${CODEX_CLIENT_ID}"
|
|
4783
|
+
${repoHeader}`;
|
|
4784
|
+
}
|
|
4785
|
+
function generateCodexEnv(token) {
|
|
4786
|
+
return `GREAT_ARROW_TOKEN=${token}
|
|
4787
|
+
`;
|
|
4788
|
+
}
|
|
4789
|
+
function extractCodexGreatArrowApiUrl(content) {
|
|
4790
|
+
const text = groupedServerText(splitTomlSections(content)).get(
|
|
4791
|
+
CODEX_SERVER_NAME
|
|
4792
|
+
);
|
|
4793
|
+
if (!text) return null;
|
|
4794
|
+
const match = text.match(/^\s*url\s*=\s*"([^"]+)"\s*$/m);
|
|
4795
|
+
if (!match?.[1]) return null;
|
|
4796
|
+
const url = match[1].trim();
|
|
4797
|
+
return url.endsWith("/api/mcp") ? url.slice(0, -"/api/mcp".length) : url.replace(/\/+$/, "");
|
|
4798
|
+
}
|
|
4799
|
+
function splitTomlSections(content) {
|
|
4800
|
+
const hasTrailingNewline = content.endsWith("\n");
|
|
4801
|
+
const lines = content.split(/\n/);
|
|
4802
|
+
if (hasTrailingNewline) lines.pop();
|
|
4803
|
+
const sections = [];
|
|
4804
|
+
let table = null;
|
|
4805
|
+
let current = [];
|
|
4806
|
+
const flush = () => {
|
|
4807
|
+
if (current.length === 0) return;
|
|
4808
|
+
sections.push({ table, text: `${current.join("\n")}
|
|
4809
|
+
` });
|
|
4810
|
+
current = [];
|
|
4811
|
+
};
|
|
4812
|
+
for (const line of lines) {
|
|
4813
|
+
const match = line.match(/^\s*\[([^\]]+)]\s*$/);
|
|
4814
|
+
if (match) {
|
|
4815
|
+
flush();
|
|
4816
|
+
table = match[1] ?? null;
|
|
4817
|
+
}
|
|
4818
|
+
current.push(line);
|
|
4819
|
+
}
|
|
4820
|
+
flush();
|
|
4821
|
+
return sections;
|
|
4822
|
+
}
|
|
4823
|
+
function tableServerName(table) {
|
|
4824
|
+
if (!table) return null;
|
|
4825
|
+
const match = table.match(/^mcp_servers\.(?:"([^"]+)"|([^.\s]+))(?:\.|$)/);
|
|
4826
|
+
return match?.[1] ?? match?.[2] ?? null;
|
|
4827
|
+
}
|
|
4828
|
+
function groupedServerText(sections) {
|
|
4829
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
4830
|
+
for (const section of sections) {
|
|
4831
|
+
const server = tableServerName(section.table);
|
|
4832
|
+
if (!server) continue;
|
|
4833
|
+
grouped.set(server, `${grouped.get(server) ?? ""}${section.text}`);
|
|
4834
|
+
}
|
|
4835
|
+
return grouped;
|
|
4836
|
+
}
|
|
4837
|
+
function removeServerSections(content, servers) {
|
|
4838
|
+
const sections = splitTomlSections(content);
|
|
4839
|
+
return sections.filter((section) => {
|
|
4840
|
+
const server = tableServerName(section.table);
|
|
4841
|
+
return !server || !servers.has(server);
|
|
4842
|
+
}).map((section) => section.text).join("").replace(/\n{3,}/g, "\n\n");
|
|
4843
|
+
}
|
|
4844
|
+
function upsertCodexServerBlock(existingContent, apiUrl, repoUrl) {
|
|
4845
|
+
const block = generateCodexConfigToml(apiUrl, repoUrl).trimEnd();
|
|
4846
|
+
const withoutGreatArrow = removeServerSections(
|
|
4847
|
+
existingContent,
|
|
4848
|
+
/* @__PURE__ */ new Set([CODEX_SERVER_NAME])
|
|
4849
|
+
).replace(/\s*$/, "");
|
|
4850
|
+
if (!withoutGreatArrow) return `${block}
|
|
4851
|
+
`;
|
|
4852
|
+
return `${withoutGreatArrow}
|
|
4853
|
+
|
|
4854
|
+
${block}
|
|
4855
|
+
`;
|
|
4856
|
+
}
|
|
4857
|
+
function extractConfiguredRepoKeys(text) {
|
|
4858
|
+
const keys = /* @__PURE__ */ new Set();
|
|
4859
|
+
const repoSetting = /(?:X-Repo-URL|MEMORY_REPO_URL|GAD_REPO_URL)\s*=\s*"([^"]+)"/gi;
|
|
4860
|
+
for (const match of text.matchAll(repoSetting)) {
|
|
4861
|
+
const key = repoKey(match[1]);
|
|
4862
|
+
if (key) keys.add(key);
|
|
4863
|
+
}
|
|
4864
|
+
const gitUrls = /(?:https:\/\/|ssh:\/\/git@|git@)(?:github\.com|gitlab\.com|bitbucket\.org)[:/][^\s"',\]]+/gi;
|
|
4865
|
+
for (const match of text.matchAll(gitUrls)) {
|
|
4866
|
+
const key = repoKey(match[0]);
|
|
4867
|
+
if (key) keys.add(key);
|
|
4868
|
+
}
|
|
4869
|
+
return [...keys];
|
|
4870
|
+
}
|
|
4871
|
+
function hasJobbyJobMarker(server, text) {
|
|
4872
|
+
return /jobby\s*job|jobbyjob|jobbyjob\.ai/i.test(`${server}
|
|
4873
|
+
${text}`);
|
|
4874
|
+
}
|
|
4875
|
+
function isUnmanagedServer(server) {
|
|
4876
|
+
return !SAFE_GLOBAL_MCP_SERVERS.has(server);
|
|
4877
|
+
}
|
|
4878
|
+
function inspectCodexConfig(content, expectedRepoUrl) {
|
|
4879
|
+
const expectedRepoKey = repoKey(expectedRepoUrl);
|
|
4880
|
+
const grouped = groupedServerText(splitTomlSections(content));
|
|
4881
|
+
const serverNames = [...grouped.keys()].sort();
|
|
4882
|
+
const greatArrowText = grouped.get(CODEX_SERVER_NAME) ?? "";
|
|
4883
|
+
const hasGreatArrowServer = grouped.has(CODEX_SERVER_NAME);
|
|
4884
|
+
const greatArrowRepoKeys = extractConfiguredRepoKeys(greatArrowText);
|
|
4885
|
+
const greatArrowHasRepoHeader = Boolean(
|
|
4886
|
+
expectedRepoKey && greatArrowRepoKeys.includes(expectedRepoKey)
|
|
4887
|
+
);
|
|
4888
|
+
const unmanagedServers = serverNames.filter(isUnmanagedServer);
|
|
4889
|
+
const staleRepoServers = [];
|
|
4890
|
+
for (const [server, text] of grouped.entries()) {
|
|
4891
|
+
if (server === CODEX_SERVER_NAME) continue;
|
|
4892
|
+
const keys = extractConfiguredRepoKeys(text);
|
|
4893
|
+
const mismatchedExplicitRepo = expectedRepoKey && keys.length > 0 && !keys.some((key) => key === expectedRepoKey);
|
|
4894
|
+
if (mismatchedExplicitRepo || hasJobbyJobMarker(server, text)) {
|
|
4895
|
+
staleRepoServers.push(server);
|
|
4896
|
+
}
|
|
4897
|
+
}
|
|
4898
|
+
staleRepoServers.sort();
|
|
4899
|
+
const issues = [];
|
|
4900
|
+
issues.push(
|
|
4901
|
+
hasGreatArrowServer ? {
|
|
4902
|
+
code: "great_arrow_present",
|
|
4903
|
+
level: "pass",
|
|
4904
|
+
message: "Codex has a Great Arrow MCP server block."
|
|
4905
|
+
} : {
|
|
4906
|
+
code: "great_arrow_missing",
|
|
4907
|
+
level: "fail",
|
|
4908
|
+
message: "Codex is missing the Great Arrow MCP server block.",
|
|
4909
|
+
hint: "Run `gad mcp install codex` from the repo root."
|
|
4910
|
+
}
|
|
4911
|
+
);
|
|
4912
|
+
if (expectedRepoKey) {
|
|
4913
|
+
issues.push(
|
|
4914
|
+
greatArrowHasRepoHeader ? {
|
|
4915
|
+
code: "repo_header_present",
|
|
4916
|
+
level: "pass",
|
|
4917
|
+
message: `Great Arrow is pinned to ${expectedRepoUrl}.`
|
|
4918
|
+
} : {
|
|
4919
|
+
code: "repo_header_missing",
|
|
4920
|
+
level: "warn",
|
|
4921
|
+
message: "Great Arrow is not pinned with X-Repo-URL.",
|
|
4922
|
+
hint: "Run `gad mcp install codex` from the repo root to add repo routing."
|
|
4923
|
+
}
|
|
4924
|
+
);
|
|
4925
|
+
}
|
|
4926
|
+
if (staleRepoServers.length > 0) {
|
|
4927
|
+
issues.push({
|
|
4928
|
+
code: "stale_repo_servers",
|
|
4929
|
+
level: "fail",
|
|
4930
|
+
message: `Found stale repo-scoped MCP server(s): ${staleRepoServers.join(", ")}.`,
|
|
4931
|
+
hint: "Run `gad doctor codex --enforce` to remove the stale server blocks."
|
|
4932
|
+
});
|
|
4933
|
+
} else {
|
|
4934
|
+
issues.push({
|
|
4935
|
+
code: "no_stale_repo_servers",
|
|
4936
|
+
level: "pass",
|
|
4937
|
+
message: "No stale repo-scoped MCP server blocks found."
|
|
4938
|
+
});
|
|
4939
|
+
}
|
|
4940
|
+
if (unmanagedServers.length > 0) {
|
|
4941
|
+
issues.push({
|
|
4942
|
+
code: "unmanaged_servers",
|
|
4943
|
+
level: "warn",
|
|
4944
|
+
message: `Codex has unmanaged MCP server(s): ${unmanagedServers.join(", ")}.`,
|
|
4945
|
+
hint: "Review these manually if they are not intentionally global."
|
|
4946
|
+
});
|
|
4947
|
+
}
|
|
4948
|
+
return {
|
|
4949
|
+
expectedRepoUrl: expectedRepoUrl ?? null,
|
|
4950
|
+
expectedRepoKey,
|
|
4951
|
+
hasGreatArrowServer,
|
|
4952
|
+
greatArrowHasRepoHeader,
|
|
4953
|
+
unmanagedServers,
|
|
4954
|
+
staleRepoServers,
|
|
4955
|
+
issues
|
|
4956
|
+
};
|
|
4957
|
+
}
|
|
4958
|
+
function repairCodexConfig(content, staleRepoServers) {
|
|
4959
|
+
if (staleRepoServers.length === 0) {
|
|
4960
|
+
return { content, removedServers: [] };
|
|
4961
|
+
}
|
|
4962
|
+
const removed = [...new Set(staleRepoServers)].sort();
|
|
4963
|
+
const repaired = removeServerSections(content, new Set(removed));
|
|
4964
|
+
return {
|
|
4965
|
+
content: repaired.endsWith("\n") ? repaired : `${repaired}
|
|
4966
|
+
`,
|
|
4967
|
+
removedServers: removed
|
|
4968
|
+
};
|
|
4969
|
+
}
|
|
4970
|
+
|
|
4637
4971
|
// src/commands/doctor.ts
|
|
4638
4972
|
function renderCheck(check, color2) {
|
|
4639
4973
|
const { status, name, message, hint } = check;
|
|
@@ -4717,8 +5051,12 @@ async function checkIntegrations(auth) {
|
|
|
4717
5051
|
const client = new GadClient({ token: auth.token, apiUrl: auth.apiUrl });
|
|
4718
5052
|
try {
|
|
4719
5053
|
const result = await client.mcpCall("integrations_list", {});
|
|
4720
|
-
const
|
|
4721
|
-
const
|
|
5054
|
+
const data = result ?? {};
|
|
5055
|
+
const integrations = Array.isArray(result) ? result : Array.isArray(data.integrations) ? data.integrations : [];
|
|
5056
|
+
const errorIntegrations = integrations.filter((i) => {
|
|
5057
|
+
const status = (i.status ?? "").toUpperCase();
|
|
5058
|
+
return status === "ERROR" || status === "NEEDS_REAUTH";
|
|
5059
|
+
});
|
|
4722
5060
|
if (errorIntegrations.length === 0) {
|
|
4723
5061
|
return {
|
|
4724
5062
|
name: "Integrations",
|
|
@@ -4730,7 +5068,7 @@ async function checkIntegrations(auth) {
|
|
|
4730
5068
|
return {
|
|
4731
5069
|
name: "Integrations",
|
|
4732
5070
|
status: "warn",
|
|
4733
|
-
message: `${errorIntegrations.length} integration(s)
|
|
5071
|
+
message: `${errorIntegrations.length} of ${integrations.length} integration(s) need attention: ${kinds}`,
|
|
4734
5072
|
hint: "Run 'gad integrations list' for details, or 'gad integrations sync <kind>' to retry."
|
|
4735
5073
|
};
|
|
4736
5074
|
} catch {
|
|
@@ -4768,18 +5106,145 @@ async function checkMcp(auth) {
|
|
|
4768
5106
|
};
|
|
4769
5107
|
}
|
|
4770
5108
|
}
|
|
5109
|
+
function defaultCodexConfigPath() {
|
|
5110
|
+
return join2(homedir2(), ".codex", "config.toml");
|
|
5111
|
+
}
|
|
5112
|
+
function codexIssuesToChecks(issues) {
|
|
5113
|
+
return issues.map((issue) => {
|
|
5114
|
+
const check = {
|
|
5115
|
+
name: `Codex Config: ${issue.code}`,
|
|
5116
|
+
status: issue.level,
|
|
5117
|
+
message: issue.server ? `${issue.message} (${issue.server})` : issue.message
|
|
5118
|
+
};
|
|
5119
|
+
if (issue.hint) check.hint = issue.hint;
|
|
5120
|
+
return check;
|
|
5121
|
+
});
|
|
5122
|
+
}
|
|
5123
|
+
function codexCommand() {
|
|
5124
|
+
return new Command("codex").description(
|
|
5125
|
+
"Check Codex MCP config for wrong-repo entries and optional repair"
|
|
5126
|
+
).option(
|
|
5127
|
+
"--config <path>",
|
|
5128
|
+
"Path to Codex config.toml",
|
|
5129
|
+
defaultCodexConfigPath()
|
|
5130
|
+
).option(
|
|
5131
|
+
"--expected-repo <url>",
|
|
5132
|
+
"Expected repo URL (defaults to current git origin)"
|
|
5133
|
+
).option(
|
|
5134
|
+
"--enforce",
|
|
5135
|
+
"Back up config, rewrite Great Arrow repo pin, and remove stale repo-scoped MCP servers"
|
|
5136
|
+
).option("--json", "Output as JSON").option("--quiet", "Suppress decorative output").option("--no-color", "Disable color output").action(
|
|
5137
|
+
async (options, command) => {
|
|
5138
|
+
const g = globalOutputOpts(command);
|
|
5139
|
+
const color2 = createColor(g.noColor);
|
|
5140
|
+
const fmt = createFormatter({
|
|
5141
|
+
json: g.json,
|
|
5142
|
+
quiet: g.quiet,
|
|
5143
|
+
isTTY: isTTY(),
|
|
5144
|
+
noColor: g.noColor
|
|
5145
|
+
});
|
|
5146
|
+
const configPath = options.config ?? defaultCodexConfigPath();
|
|
5147
|
+
const expectedRepoUrl = options.expectedRepo ?? detectGitOrigin2();
|
|
5148
|
+
let content = existsSync(configPath) ? await readFile2(configPath, "utf8") : "";
|
|
5149
|
+
let backupPath = null;
|
|
5150
|
+
let removedServers = [];
|
|
5151
|
+
if (options.enforce) {
|
|
5152
|
+
const apiUrl = extractCodexGreatArrowApiUrl(content) ?? (await resolveAuth()).apiUrl;
|
|
5153
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
5154
|
+
await mkdir2(dirname2(configPath), { recursive: true });
|
|
5155
|
+
if (content.trim()) {
|
|
5156
|
+
backupPath = `${configPath}.bak.${timestamp}`;
|
|
5157
|
+
await copyFile(configPath, backupPath);
|
|
5158
|
+
}
|
|
5159
|
+
content = upsertCodexServerBlock(content, apiUrl, expectedRepoUrl);
|
|
5160
|
+
const preRepair = inspectCodexConfig(content, expectedRepoUrl);
|
|
5161
|
+
const repaired = repairCodexConfig(
|
|
5162
|
+
content,
|
|
5163
|
+
preRepair.staleRepoServers
|
|
5164
|
+
);
|
|
5165
|
+
content = repaired.content;
|
|
5166
|
+
removedServers = repaired.removedServers;
|
|
5167
|
+
await writeFile2(configPath, content, "utf8");
|
|
5168
|
+
}
|
|
5169
|
+
const inspection = inspectCodexConfig(content, expectedRepoUrl);
|
|
5170
|
+
const checks = codexIssuesToChecks(inspection.issues);
|
|
5171
|
+
if (options.enforce) {
|
|
5172
|
+
const enforcementCheck = {
|
|
5173
|
+
name: "Codex Config: enforcement",
|
|
5174
|
+
status: "pass",
|
|
5175
|
+
message: removedServers.length > 0 ? `Removed stale server(s): ${removedServers.join(", ")}` : "No stale server blocks needed removal"
|
|
5176
|
+
};
|
|
5177
|
+
if (backupPath) {
|
|
5178
|
+
enforcementCheck.hint = `Backup written to ${backupPath}`;
|
|
5179
|
+
}
|
|
5180
|
+
checks.push(enforcementCheck);
|
|
5181
|
+
}
|
|
5182
|
+
if (g.json) {
|
|
5183
|
+
process.stdout.write(
|
|
5184
|
+
JSON.stringify(
|
|
5185
|
+
{
|
|
5186
|
+
configPath,
|
|
5187
|
+
expectedRepoUrl,
|
|
5188
|
+
enforce: Boolean(options.enforce),
|
|
5189
|
+
backupPath,
|
|
5190
|
+
removedServers,
|
|
5191
|
+
inspection,
|
|
5192
|
+
checks
|
|
5193
|
+
},
|
|
5194
|
+
null,
|
|
5195
|
+
2
|
|
5196
|
+
) + "\n"
|
|
5197
|
+
);
|
|
5198
|
+
} else {
|
|
5199
|
+
if (!expectedRepoUrl) {
|
|
5200
|
+
process.stdout.write(
|
|
5201
|
+
renderCheck(
|
|
5202
|
+
{
|
|
5203
|
+
name: "Codex Config: repo detection",
|
|
5204
|
+
status: "warn",
|
|
5205
|
+
message: "No git origin detected for this directory",
|
|
5206
|
+
hint: "Run from a repo root or pass --expected-repo."
|
|
5207
|
+
},
|
|
5208
|
+
color2
|
|
5209
|
+
) + "\n"
|
|
5210
|
+
);
|
|
5211
|
+
}
|
|
5212
|
+
for (const check of checks) {
|
|
5213
|
+
process.stdout.write(renderCheck(check, color2) + "\n");
|
|
5214
|
+
}
|
|
5215
|
+
if (!g.quiet) {
|
|
5216
|
+
const failed = checks.filter((c) => c.status === "fail").length;
|
|
5217
|
+
const warned = checks.filter((c) => c.status === "warn").length;
|
|
5218
|
+
process.stdout.write("\n");
|
|
5219
|
+
if (failed === 0 && warned === 0) {
|
|
5220
|
+
fmt.success("Codex config guard passed");
|
|
5221
|
+
} else if (failed === 0) {
|
|
5222
|
+
fmt.warn(`Codex config guard passed with ${warned} warning(s)`);
|
|
5223
|
+
} else {
|
|
5224
|
+
fmt.error(
|
|
5225
|
+
`Codex config guard found ${failed} failure(s), ${warned} warning(s)`
|
|
5226
|
+
);
|
|
5227
|
+
}
|
|
5228
|
+
}
|
|
5229
|
+
}
|
|
5230
|
+
if (checks.some((check) => check.status === "fail")) {
|
|
5231
|
+
process.exitCode = 1;
|
|
5232
|
+
}
|
|
5233
|
+
}
|
|
5234
|
+
);
|
|
5235
|
+
}
|
|
4771
5236
|
function doctorCommand() {
|
|
4772
|
-
const cmd = new Command("doctor").description("Run a comprehensive health check").option("--json", "Output as JSON").option("--quiet", "Suppress decorative output").option("--no-color", "Disable color output").action(async (options) => {
|
|
4773
|
-
const
|
|
4774
|
-
const color2 = createColor(noColor);
|
|
5237
|
+
const cmd = new Command("doctor").description("Run a comprehensive health check").option("--json", "Output as JSON").option("--quiet", "Suppress decorative output").option("--no-color", "Disable color output").action(async (options, command) => {
|
|
5238
|
+
const g = globalOutputOpts(command);
|
|
5239
|
+
const color2 = createColor(g.noColor);
|
|
4775
5240
|
const fmt = createFormatter({
|
|
4776
|
-
json:
|
|
4777
|
-
quiet:
|
|
5241
|
+
json: g.json,
|
|
5242
|
+
quiet: g.quiet,
|
|
4778
5243
|
isTTY: isTTY(),
|
|
4779
|
-
noColor
|
|
5244
|
+
noColor: g.noColor
|
|
4780
5245
|
});
|
|
4781
5246
|
const checks = [];
|
|
4782
|
-
const isJson =
|
|
5247
|
+
const isJson = g.json;
|
|
4783
5248
|
const { check: tokenCheck, auth } = await checkToken();
|
|
4784
5249
|
checks.push(tokenCheck);
|
|
4785
5250
|
if (!isJson) {
|
|
@@ -4802,7 +5267,7 @@ function doctorCommand() {
|
|
|
4802
5267
|
}
|
|
4803
5268
|
if (isJson) {
|
|
4804
5269
|
process.stdout.write(JSON.stringify(checks) + "\n");
|
|
4805
|
-
} else if (!
|
|
5270
|
+
} else if (!g.quiet) {
|
|
4806
5271
|
const passed = checks.filter((c) => c.status === "pass").length;
|
|
4807
5272
|
const warned = checks.filter((c) => c.status === "warn").length;
|
|
4808
5273
|
const failed = checks.filter((c) => c.status === "fail").length;
|
|
@@ -4816,6 +5281,7 @@ function doctorCommand() {
|
|
|
4816
5281
|
}
|
|
4817
5282
|
}
|
|
4818
5283
|
});
|
|
5284
|
+
cmd.addCommand(codexCommand());
|
|
4819
5285
|
return cmd;
|
|
4820
5286
|
}
|
|
4821
5287
|
|
|
@@ -4865,19 +5331,6 @@ async function createClient2() {
|
|
|
4865
5331
|
const auth = await resolveAuth();
|
|
4866
5332
|
return new GadClient({ token: auth.token, apiUrl: auth.apiUrl });
|
|
4867
5333
|
}
|
|
4868
|
-
function extractMcpContent2(result) {
|
|
4869
|
-
if (Array.isArray(result) && result.length > 0) {
|
|
4870
|
-
const first = result[0];
|
|
4871
|
-
if (typeof first === "object" && first !== null && "type" in first && first.type === "text" && "text" in first) {
|
|
4872
|
-
try {
|
|
4873
|
-
return JSON.parse(first.text);
|
|
4874
|
-
} catch {
|
|
4875
|
-
return first.text;
|
|
4876
|
-
}
|
|
4877
|
-
}
|
|
4878
|
-
}
|
|
4879
|
-
return result;
|
|
4880
|
-
}
|
|
4881
5334
|
function formatErrorDetail(rawError) {
|
|
4882
5335
|
if (rawError === null || rawError === void 0) return null;
|
|
4883
5336
|
if (typeof rawError === "string") return rawError;
|
|
@@ -4927,8 +5380,9 @@ function handleSyncResponse(fmt, kind, response) {
|
|
|
4927
5380
|
}
|
|
4928
5381
|
}
|
|
4929
5382
|
var listCommand3 = new Command("list").description("List all connected integrations with sync status").option("--json", "Output as JSON").action(async (options) => {
|
|
4930
|
-
const
|
|
4931
|
-
|
|
5383
|
+
const jsonFlag = Boolean(
|
|
5384
|
+
options.json || listCommand3.optsWithGlobals().json
|
|
5385
|
+
);
|
|
4932
5386
|
const fmt = createFormatter({
|
|
4933
5387
|
json: jsonFlag,
|
|
4934
5388
|
quiet: false,
|
|
@@ -4937,7 +5391,7 @@ var listCommand3 = new Command("list").description("List all connected integrati
|
|
|
4937
5391
|
});
|
|
4938
5392
|
const client = await createClient2();
|
|
4939
5393
|
const result = await client.mcpCall("integrations_list", {});
|
|
4940
|
-
const data =
|
|
5394
|
+
const data = result;
|
|
4941
5395
|
let integrations = [];
|
|
4942
5396
|
if (Array.isArray(data)) {
|
|
4943
5397
|
integrations = data;
|
|
@@ -4964,7 +5418,7 @@ var syncCommand = new Command("sync").description("Trigger a manual sync for the
|
|
|
4964
5418
|
fmt.info(`Triggering sync for "${kind}"...`);
|
|
4965
5419
|
try {
|
|
4966
5420
|
const result = await client.mcpCall("integration_sync", { kind });
|
|
4967
|
-
const data =
|
|
5421
|
+
const data = result;
|
|
4968
5422
|
if (typeof data === "object" && data !== null) {
|
|
4969
5423
|
handleSyncResponse(fmt, kind, data);
|
|
4970
5424
|
} else {
|
|
@@ -4990,39 +5444,20 @@ var SUPPORTED_CLIENTS = [
|
|
|
4990
5444
|
"codex",
|
|
4991
5445
|
"gemini"
|
|
4992
5446
|
];
|
|
4993
|
-
var MCP_SERVER_NAME =
|
|
5447
|
+
var MCP_SERVER_NAME = CODEX_SERVER_NAME;
|
|
4994
5448
|
var CLIENT_IDS = {
|
|
4995
5449
|
claude: "claude-code-cli",
|
|
4996
5450
|
cursor: "cursor",
|
|
4997
5451
|
kiro: "kiro",
|
|
4998
|
-
codex:
|
|
5452
|
+
codex: CODEX_CLIENT_ID,
|
|
4999
5453
|
gemini: "gemini-cli"
|
|
5000
5454
|
};
|
|
5001
|
-
function normalizeApiUrl(apiUrl) {
|
|
5002
|
-
return apiUrl.replace(/\/+$/, "");
|
|
5003
|
-
}
|
|
5004
|
-
function mcpEndpoint(apiUrl) {
|
|
5005
|
-
return `${normalizeApiUrl(apiUrl)}/api/mcp`;
|
|
5006
|
-
}
|
|
5007
5455
|
async function createClient3() {
|
|
5008
5456
|
const auth = await resolveAuth();
|
|
5009
5457
|
const client = new GadClient({ token: auth.token, apiUrl: auth.apiUrl });
|
|
5010
5458
|
return { client, apiUrl: auth.apiUrl };
|
|
5011
5459
|
}
|
|
5012
|
-
function
|
|
5013
|
-
if (Array.isArray(result) && result.length > 0) {
|
|
5014
|
-
const first = result[0];
|
|
5015
|
-
if (typeof first === "object" && first !== null && "type" in first && first.type === "text" && "text" in first) {
|
|
5016
|
-
try {
|
|
5017
|
-
return JSON.parse(first.text);
|
|
5018
|
-
} catch {
|
|
5019
|
-
return first.text;
|
|
5020
|
-
}
|
|
5021
|
-
}
|
|
5022
|
-
}
|
|
5023
|
-
return result;
|
|
5024
|
-
}
|
|
5025
|
-
function generateMcpConfig(client, apiUrl, token) {
|
|
5460
|
+
function generateMcpConfig(client, apiUrl, token, repoUrl) {
|
|
5026
5461
|
const endpoint = mcpEndpoint(apiUrl);
|
|
5027
5462
|
if (client === "codex") {
|
|
5028
5463
|
return {
|
|
@@ -5036,7 +5471,11 @@ function generateMcpConfig(client, apiUrl, token) {
|
|
|
5036
5471
|
MEMORY_API_URL: apiUrl,
|
|
5037
5472
|
MEMORY_CLIENT_NAME: CLIENT_IDS.codex,
|
|
5038
5473
|
GAD_TOKEN: token,
|
|
5039
|
-
GAD_API_URL: apiUrl
|
|
5474
|
+
GAD_API_URL: apiUrl,
|
|
5475
|
+
...repoUrl ? {
|
|
5476
|
+
MEMORY_REPO_URL: repoUrl,
|
|
5477
|
+
GAD_REPO_URL: repoUrl
|
|
5478
|
+
} : {}
|
|
5040
5479
|
}
|
|
5041
5480
|
}
|
|
5042
5481
|
}
|
|
@@ -5049,44 +5488,13 @@ function generateMcpConfig(client, apiUrl, token) {
|
|
|
5049
5488
|
url: endpoint,
|
|
5050
5489
|
headers: {
|
|
5051
5490
|
Authorization: `Bearer ${token}`,
|
|
5052
|
-
"X-MCP-Client": CLIENT_IDS[client]
|
|
5491
|
+
"X-MCP-Client": CLIENT_IDS[client],
|
|
5492
|
+
...repoUrl ? { "X-Repo-URL": repoUrl } : {}
|
|
5053
5493
|
}
|
|
5054
5494
|
}
|
|
5055
5495
|
}
|
|
5056
5496
|
};
|
|
5057
5497
|
}
|
|
5058
|
-
function tomlEscape(value) {
|
|
5059
|
-
return value.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
|
|
5060
|
-
}
|
|
5061
|
-
function generateCodexConfigToml(apiUrl) {
|
|
5062
|
-
return `[mcp_servers.${MCP_SERVER_NAME}]
|
|
5063
|
-
transport = "streamable_http"
|
|
5064
|
-
url = "${tomlEscape(mcpEndpoint(apiUrl))}"
|
|
5065
|
-
bearer_token_env_var = "GREAT_ARROW_TOKEN"
|
|
5066
|
-
`;
|
|
5067
|
-
}
|
|
5068
|
-
function generateCodexEnv(token) {
|
|
5069
|
-
return `GREAT_ARROW_TOKEN=${token}
|
|
5070
|
-
`;
|
|
5071
|
-
}
|
|
5072
|
-
function upsertCodexServerBlock(existingContent, apiUrl) {
|
|
5073
|
-
const block = generateCodexConfigToml(apiUrl).trimEnd();
|
|
5074
|
-
const serverBlockPattern = /^\[mcp_servers\.great-arrow\]\n[\s\S]*?(?=^\[|\s*$)/m;
|
|
5075
|
-
if (!existingContent.trim()) {
|
|
5076
|
-
return `${block}
|
|
5077
|
-
`;
|
|
5078
|
-
}
|
|
5079
|
-
if (serverBlockPattern.test(existingContent)) {
|
|
5080
|
-
const updated = existingContent.replace(serverBlockPattern, block);
|
|
5081
|
-
return updated.endsWith("\n") ? updated : `${updated}
|
|
5082
|
-
`;
|
|
5083
|
-
}
|
|
5084
|
-
const trimmedRight = existingContent.replace(/\s*$/, "");
|
|
5085
|
-
return `${trimmedRight}
|
|
5086
|
-
|
|
5087
|
-
${block}
|
|
5088
|
-
`;
|
|
5089
|
-
}
|
|
5090
5498
|
function upsertEnvValue(existingContent, key, value) {
|
|
5091
5499
|
const lines = existingContent.split(/\r?\n/).filter((line) => line.trim() !== "" && !line.startsWith(`${key}=`));
|
|
5092
5500
|
lines.push(`${key}=${value}`);
|
|
@@ -5102,29 +5510,29 @@ async function pathExists(path) {
|
|
|
5102
5510
|
return false;
|
|
5103
5511
|
}
|
|
5104
5512
|
}
|
|
5105
|
-
async function installCodexConfig(apiUrl, token) {
|
|
5106
|
-
const { homedir:
|
|
5107
|
-
const { join:
|
|
5108
|
-
const { chmod, copyFile, mkdir:
|
|
5109
|
-
const codexDir =
|
|
5110
|
-
const configPath =
|
|
5111
|
-
const envPath =
|
|
5513
|
+
async function installCodexConfig(apiUrl, token, repoUrl) {
|
|
5514
|
+
const { homedir: homedir3 } = await import("node:os");
|
|
5515
|
+
const { join: join3 } = await import("node:path");
|
|
5516
|
+
const { chmod, copyFile: copyFile2, mkdir: mkdir3, readFile: readFile3, writeFile: writeFile3 } = await import("node:fs/promises");
|
|
5517
|
+
const codexDir = join3(homedir3(), ".codex");
|
|
5518
|
+
const configPath = join3(codexDir, "config.toml");
|
|
5519
|
+
const envPath = join3(codexDir, ".env");
|
|
5112
5520
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
5113
|
-
await
|
|
5114
|
-
const existingConfig = await pathExists(configPath) ? await
|
|
5521
|
+
await mkdir3(codexDir, { recursive: true });
|
|
5522
|
+
const existingConfig = await pathExists(configPath) ? await readFile3(configPath, "utf8") : "";
|
|
5115
5523
|
if (existingConfig.trim()) {
|
|
5116
|
-
await
|
|
5524
|
+
await copyFile2(configPath, `${configPath}.bak.${timestamp}`);
|
|
5117
5525
|
}
|
|
5118
|
-
await
|
|
5526
|
+
await writeFile3(
|
|
5119
5527
|
configPath,
|
|
5120
|
-
upsertCodexServerBlock(existingConfig, apiUrl),
|
|
5528
|
+
upsertCodexServerBlock(existingConfig, apiUrl, repoUrl),
|
|
5121
5529
|
"utf8"
|
|
5122
5530
|
);
|
|
5123
|
-
const existingEnv = await pathExists(envPath) ? await
|
|
5531
|
+
const existingEnv = await pathExists(envPath) ? await readFile3(envPath, "utf8") : "";
|
|
5124
5532
|
if (existingEnv.trim()) {
|
|
5125
|
-
await
|
|
5533
|
+
await copyFile2(envPath, `${envPath}.bak.${timestamp}`);
|
|
5126
5534
|
}
|
|
5127
|
-
await
|
|
5535
|
+
await writeFile3(
|
|
5128
5536
|
envPath,
|
|
5129
5537
|
upsertEnvValue(existingEnv, "GREAT_ARROW_TOKEN", token),
|
|
5130
5538
|
"utf8"
|
|
@@ -5141,8 +5549,9 @@ var statusColumns = [
|
|
|
5141
5549
|
var statusCommand = new Command("status").description(
|
|
5142
5550
|
"Show MCP server connection status, proxy version, and session count"
|
|
5143
5551
|
).option("--json", "Output as JSON").action(async (options) => {
|
|
5144
|
-
const
|
|
5145
|
-
|
|
5552
|
+
const jsonFlag = Boolean(
|
|
5553
|
+
options.json || statusCommand.optsWithGlobals().json
|
|
5554
|
+
);
|
|
5146
5555
|
const fmt = createFormatter({
|
|
5147
5556
|
json: jsonFlag,
|
|
5148
5557
|
quiet: false,
|
|
@@ -5158,7 +5567,7 @@ var statusCommand = new Command("status").description(
|
|
|
5158
5567
|
};
|
|
5159
5568
|
try {
|
|
5160
5569
|
const result = await client.mcpCall("workspace_introspect", {});
|
|
5161
|
-
const data =
|
|
5570
|
+
const data = result;
|
|
5162
5571
|
if (typeof data === "object" && data !== null) {
|
|
5163
5572
|
const info = data;
|
|
5164
5573
|
statusData = {
|
|
@@ -5197,8 +5606,9 @@ function renderLogEntry(entry, c) {
|
|
|
5197
5606
|
`);
|
|
5198
5607
|
}
|
|
5199
5608
|
var logsCommand = new Command("logs").description("Tail MCP connection log with colorized output").option("--lines <n>", "Number of recent log lines to show", "20").option("--json", "Output as JSON").action(async (options) => {
|
|
5200
|
-
const
|
|
5201
|
-
|
|
5609
|
+
const jsonFlag = Boolean(
|
|
5610
|
+
options.json || logsCommand.optsWithGlobals().json
|
|
5611
|
+
);
|
|
5202
5612
|
const fmt = createFormatter({
|
|
5203
5613
|
json: jsonFlag,
|
|
5204
5614
|
quiet: false,
|
|
@@ -5212,7 +5622,7 @@ var logsCommand = new Command("logs").description("Tail MCP connection log with
|
|
|
5212
5622
|
const result = await client.mcpCall("integration_sync_logs", {
|
|
5213
5623
|
limit: lineCount
|
|
5214
5624
|
});
|
|
5215
|
-
const data =
|
|
5625
|
+
const data = result;
|
|
5216
5626
|
if (jsonFlag) {
|
|
5217
5627
|
fmt.table(Array.isArray(data) ? data : [], [
|
|
5218
5628
|
{ key: "timestamp", header: "Timestamp" },
|
|
@@ -5278,7 +5688,7 @@ async function checkJsonRpc(client, verbose, c) {
|
|
|
5278
5688
|
if (verbose) process.stdout.write(`${c.dim("\u2192")} JSON-RPC handshake... `);
|
|
5279
5689
|
try {
|
|
5280
5690
|
const result = await client.mcpCall("mcp_capabilities", {});
|
|
5281
|
-
const data =
|
|
5691
|
+
const data = result;
|
|
5282
5692
|
let toolCount = "ok";
|
|
5283
5693
|
if (typeof data === "object" && data !== null) {
|
|
5284
5694
|
const record = data;
|
|
@@ -5300,8 +5710,9 @@ async function checkJsonRpc(client, verbose, c) {
|
|
|
5300
5710
|
var testCommand = new Command("test").description(
|
|
5301
5711
|
"Perform full MCP handshake diagnostic (DNS, edge ping, JSON-RPC)"
|
|
5302
5712
|
).option("--json", "Output as JSON").action(async (options) => {
|
|
5303
|
-
const
|
|
5304
|
-
|
|
5713
|
+
const jsonFlag = Boolean(
|
|
5714
|
+
options.json || testCommand.optsWithGlobals().json
|
|
5715
|
+
);
|
|
5305
5716
|
const fmt = createFormatter({
|
|
5306
5717
|
json: jsonFlag,
|
|
5307
5718
|
quiet: false,
|
|
@@ -5336,67 +5747,75 @@ ${summary}.
|
|
|
5336
5747
|
var installCommand = new Command("install").description("Generate MCP configuration for a supported AI client").argument(
|
|
5337
5748
|
"<client>",
|
|
5338
5749
|
`AI client to configure (${SUPPORTED_CLIENTS.join(", ")})`
|
|
5339
|
-
).option("--stdout", "Print config to stdout instead of writing file").
|
|
5340
|
-
|
|
5341
|
-
|
|
5342
|
-
|
|
5343
|
-
|
|
5344
|
-
|
|
5345
|
-
|
|
5346
|
-
|
|
5347
|
-
|
|
5348
|
-
|
|
5349
|
-
);
|
|
5350
|
-
|
|
5351
|
-
|
|
5352
|
-
|
|
5353
|
-
|
|
5354
|
-
|
|
5355
|
-
|
|
5750
|
+
).option("--stdout", "Print config to stdout instead of writing file").option(
|
|
5751
|
+
"--repo-url <url>",
|
|
5752
|
+
"Repo URL to pin MCP requests to (defaults to current git origin)"
|
|
5753
|
+
).action(
|
|
5754
|
+
async (clientName, options) => {
|
|
5755
|
+
const fmt = createFormatter({
|
|
5756
|
+
json: false,
|
|
5757
|
+
quiet: false,
|
|
5758
|
+
isTTY: isTTY(),
|
|
5759
|
+
noColor: false
|
|
5760
|
+
});
|
|
5761
|
+
if (!SUPPORTED_CLIENTS.includes(clientName)) {
|
|
5762
|
+
fmt.error(
|
|
5763
|
+
`Unsupported client: "${clientName}". Supported clients: ${SUPPORTED_CLIENTS.join(", ")}`
|
|
5764
|
+
);
|
|
5765
|
+
process.exit(3);
|
|
5766
|
+
}
|
|
5767
|
+
const auth = await resolveAuth();
|
|
5768
|
+
const repoUrl = options.repoUrl ?? detectGitOrigin2();
|
|
5769
|
+
if (clientName === "codex") {
|
|
5770
|
+
if (options.stdout) {
|
|
5771
|
+
process.stdout.write(`# ~/.codex/config.toml
|
|
5356
5772
|
`);
|
|
5357
|
-
|
|
5358
|
-
|
|
5773
|
+
process.stdout.write(generateCodexConfigToml(auth.apiUrl, repoUrl));
|
|
5774
|
+
process.stdout.write(`
|
|
5359
5775
|
# ~/.codex/.env
|
|
5360
5776
|
`);
|
|
5361
|
-
|
|
5777
|
+
process.stdout.write(generateCodexEnv(auth.token));
|
|
5778
|
+
return;
|
|
5779
|
+
}
|
|
5780
|
+
const { configPath, envPath } = await installCodexConfig(
|
|
5781
|
+
auth.apiUrl,
|
|
5782
|
+
auth.token,
|
|
5783
|
+
repoUrl
|
|
5784
|
+
);
|
|
5785
|
+
fmt.success(`Wrote Codex MCP config to ${configPath} and ${envPath}`);
|
|
5362
5786
|
return;
|
|
5363
5787
|
}
|
|
5364
|
-
const
|
|
5788
|
+
const config = generateMcpConfig(
|
|
5789
|
+
clientName,
|
|
5365
5790
|
auth.apiUrl,
|
|
5366
|
-
auth.token
|
|
5791
|
+
auth.token,
|
|
5792
|
+
repoUrl
|
|
5367
5793
|
);
|
|
5368
|
-
|
|
5369
|
-
|
|
5370
|
-
|
|
5371
|
-
|
|
5372
|
-
|
|
5373
|
-
|
|
5374
|
-
|
|
5375
|
-
|
|
5376
|
-
|
|
5377
|
-
|
|
5378
|
-
process.stdout.write(configJson + "\n");
|
|
5379
|
-
return;
|
|
5794
|
+
const configJson = JSON.stringify(config, null, 2);
|
|
5795
|
+
if (options.stdout) {
|
|
5796
|
+
process.stdout.write(configJson + "\n");
|
|
5797
|
+
return;
|
|
5798
|
+
}
|
|
5799
|
+
const { writeFile: writeFile3 } = await import("node:fs/promises");
|
|
5800
|
+
const { join: join3 } = await import("node:path");
|
|
5801
|
+
const outputPath = join3(process.cwd(), ".mcp.json");
|
|
5802
|
+
await writeFile3(outputPath, configJson + "\n", "utf-8");
|
|
5803
|
+
fmt.success(`Wrote ${clientName} MCP config to .mcp.json`);
|
|
5380
5804
|
}
|
|
5381
|
-
|
|
5382
|
-
const { join: join2 } = await import("node:path");
|
|
5383
|
-
const outputPath = join2(process.cwd(), ".mcp.json");
|
|
5384
|
-
await writeFile2(outputPath, configJson + "\n", "utf-8");
|
|
5385
|
-
fmt.success(`Wrote ${clientName} MCP config to .mcp.json`);
|
|
5386
|
-
});
|
|
5805
|
+
);
|
|
5387
5806
|
var mcpCommand = new Command("mcp").description("Manage and diagnose the MCP server connection").addCommand(statusCommand).addCommand(logsCommand).addCommand(testCommand).addCommand(installCommand);
|
|
5388
5807
|
var mcp_default = mcpCommand;
|
|
5389
5808
|
|
|
5390
5809
|
// src/commands/deploy.ts
|
|
5391
5810
|
var DEPLOY_TIMEOUT_MS = 9e4;
|
|
5392
5811
|
function deployCommand() {
|
|
5393
|
-
const cmd = new Command("deploy").description("Trigger a production deployment").option("--confirm", "Skip interactive confirmation (required in CI)").option("--json", "Output as JSON").option("--quiet", "Suppress decorative output").option("--no-color", "Disable color output").action(async (options) => {
|
|
5394
|
-
const
|
|
5812
|
+
const cmd = new Command("deploy").description("Trigger a production deployment").option("--confirm", "Skip interactive confirmation (required in CI)").option("--json", "Output as JSON").option("--quiet", "Suppress decorative output").option("--no-color", "Disable color output").action(async (options, command) => {
|
|
5813
|
+
const g = globalOutputOpts(command);
|
|
5395
5814
|
const fmt = createFormatter({
|
|
5396
|
-
json:
|
|
5397
|
-
quiet:
|
|
5815
|
+
json: g.json,
|
|
5816
|
+
quiet: g.quiet,
|
|
5398
5817
|
isTTY: isTTY(),
|
|
5399
|
-
noColor
|
|
5818
|
+
noColor: g.noColor
|
|
5400
5819
|
});
|
|
5401
5820
|
if (!options.confirm) {
|
|
5402
5821
|
if (!isTTY()) {
|
|
@@ -5424,7 +5843,7 @@ function deployCommand() {
|
|
|
5424
5843
|
fmt.info("Deploying to production...");
|
|
5425
5844
|
}
|
|
5426
5845
|
const result = await client.mcpCall("deploy_to_vercel", {});
|
|
5427
|
-
if (
|
|
5846
|
+
if (g.json) {
|
|
5428
5847
|
process.stdout.write(JSON.stringify(result) + "\n");
|
|
5429
5848
|
return;
|
|
5430
5849
|
}
|
|
@@ -5484,11 +5903,12 @@ function displayResults(result, fmt) {
|
|
|
5484
5903
|
]);
|
|
5485
5904
|
}
|
|
5486
5905
|
}
|
|
5487
|
-
var migrateCommand = new Command("migrate").description("Run pending database migrations").option("--confirm", "Skip confirmation prompt (required in non-TTY)").option("--json", "Output results as JSON").option("--quiet", "Suppress decorative output").action(async (opts) => {
|
|
5906
|
+
var migrateCommand = new Command("migrate").description("Run pending database migrations").option("--confirm", "Skip confirmation prompt (required in non-TTY)").option("--json", "Output results as JSON").option("--quiet", "Suppress decorative output").action(async (opts, command) => {
|
|
5907
|
+
const g = globalOutputOpts(command);
|
|
5488
5908
|
const tty = isTTY();
|
|
5489
5909
|
const fmt = createFormatter({
|
|
5490
|
-
json:
|
|
5491
|
-
quiet:
|
|
5910
|
+
json: g.json,
|
|
5911
|
+
quiet: g.quiet,
|
|
5492
5912
|
isTTY: tty,
|
|
5493
5913
|
noColor: !tty
|
|
5494
5914
|
});
|
|
@@ -5500,7 +5920,7 @@ var migrateCommand = new Command("migrate").description("Run pending database mi
|
|
|
5500
5920
|
const client = new GadClient({ token: auth.token, apiUrl: auth.apiUrl });
|
|
5501
5921
|
fmt.info("Running pending migrations\u2026");
|
|
5502
5922
|
const result = await client.mcpCall("apply_migration", {});
|
|
5503
|
-
if (
|
|
5923
|
+
if (g.json) {
|
|
5504
5924
|
const applied = result?.applied ?? 0;
|
|
5505
5925
|
const status = result?.status ?? "complete";
|
|
5506
5926
|
fmt.table(
|
|
@@ -5590,17 +6010,17 @@ function outputJsonSummary(results, totalDurationMs) {
|
|
|
5590
6010
|
process.stdout.write(JSON.stringify(output) + "\n");
|
|
5591
6011
|
}
|
|
5592
6012
|
function smokeCommand() {
|
|
5593
|
-
const cmd = new Command("smoke").description("Run MCP smoke tests to verify platform connectivity").option("--json", "Output as JSON").option("--quiet", "Suppress decorative output").option("--no-color", "Disable color output").action(async (options) => {
|
|
5594
|
-
const
|
|
5595
|
-
const color2 = createColor(noColor);
|
|
6013
|
+
const cmd = new Command("smoke").description("Run MCP smoke tests to verify platform connectivity").option("--json", "Output as JSON").option("--quiet", "Suppress decorative output").option("--no-color", "Disable color output").action(async (options, command) => {
|
|
6014
|
+
const g = globalOutputOpts(command);
|
|
6015
|
+
const color2 = createColor(g.noColor);
|
|
5596
6016
|
const fmt = createFormatter({
|
|
5597
|
-
json:
|
|
5598
|
-
quiet:
|
|
6017
|
+
json: g.json,
|
|
6018
|
+
quiet: g.quiet,
|
|
5599
6019
|
isTTY: isTTY(),
|
|
5600
|
-
noColor
|
|
6020
|
+
noColor: g.noColor
|
|
5601
6021
|
});
|
|
5602
|
-
const isJson =
|
|
5603
|
-
const isQuiet =
|
|
6022
|
+
const isJson = g.json;
|
|
6023
|
+
const isQuiet = g.quiet;
|
|
5604
6024
|
const auth = await resolveAuth();
|
|
5605
6025
|
const client = new GadClient({
|
|
5606
6026
|
token: auth.token,
|
|
@@ -5656,7 +6076,8 @@ async function buildClient2() {
|
|
|
5656
6076
|
var agentCommand = new Command("agent").description(
|
|
5657
6077
|
"List and invoke AI agents"
|
|
5658
6078
|
);
|
|
5659
|
-
agentCommand.command("list").description("Display available specialist and custom agents").option("--json", "Output as JSON").action(async (opts) => {
|
|
6079
|
+
agentCommand.command("list").description("Display available specialist and custom agents").option("--json", "Output as JSON").action(async (opts, command) => {
|
|
6080
|
+
const g = globalOutputOpts(command);
|
|
5660
6081
|
const client = await buildClient2();
|
|
5661
6082
|
const result = await client.mcpCall("agent_list", {});
|
|
5662
6083
|
let agents;
|
|
@@ -5667,14 +6088,15 @@ agentCommand.command("list").description("Display available specialist and custo
|
|
|
5667
6088
|
agents = data?.agents ?? data?.results ?? [];
|
|
5668
6089
|
}
|
|
5669
6090
|
const fmt = createFormatter({
|
|
5670
|
-
json:
|
|
6091
|
+
json: g.json,
|
|
5671
6092
|
quiet: false,
|
|
5672
6093
|
isTTY: isTTY(),
|
|
5673
6094
|
noColor: false
|
|
5674
6095
|
});
|
|
5675
6096
|
fmt.table(agents, agentListColumns);
|
|
5676
6097
|
});
|
|
5677
|
-
agentCommand.command("invoke <agent-id>").description("Execute an agent with a task").requiredOption("--task <task>", "Task description for the agent").option("--json", "Output raw agent response as JSON").option("--context <context>", "Additional context for the agent").action(async (agentId, opts) => {
|
|
6098
|
+
agentCommand.command("invoke <agent-id>").description("Execute an agent with a task").requiredOption("--task <task>", "Task description for the agent").option("--json", "Output raw agent response as JSON").option("--context <context>", "Additional context for the agent").action(async (agentId, opts, command) => {
|
|
6099
|
+
const g = globalOutputOpts(command);
|
|
5678
6100
|
const client = await buildClient2();
|
|
5679
6101
|
const args = {
|
|
5680
6102
|
agentId,
|
|
@@ -5684,7 +6106,7 @@ agentCommand.command("invoke <agent-id>").description("Execute an agent with a t
|
|
|
5684
6106
|
args.context = opts.context;
|
|
5685
6107
|
}
|
|
5686
6108
|
const result = await client.mcpCall("agent_invoke", args, AGENT_TIMEOUT_MS);
|
|
5687
|
-
if (
|
|
6109
|
+
if (g.json) {
|
|
5688
6110
|
const output = typeof result === "string" ? result : JSON.stringify(result, null, 2);
|
|
5689
6111
|
process.stdout.write(output + "\n");
|
|
5690
6112
|
return;
|
|
@@ -5720,15 +6142,16 @@ function extractAnswer(data) {
|
|
|
5720
6142
|
return result.answer || result.text || result.result || result.content || "";
|
|
5721
6143
|
}
|
|
5722
6144
|
function askCommand() {
|
|
5723
|
-
const cmd = new Command("ask").description("Ask the orchestrator a question").argument("<question>", "The question to ask").option("--json", "Output raw response as JSON").option("--quiet", "Suppress decorative output").option("--no-color", "Disable color output").action(async (question, options) => {
|
|
6145
|
+
const cmd = new Command("ask").description("Ask the orchestrator a question").argument("<question>", "The question to ask").option("--json", "Output raw response as JSON").option("--quiet", "Suppress decorative output").option("--no-color", "Disable color output").action(async (question, options, command) => {
|
|
6146
|
+
const g = globalOutputOpts(command);
|
|
5724
6147
|
const auth = await resolveAuth();
|
|
5725
6148
|
const client = new GadClient({
|
|
5726
6149
|
token: auth.token,
|
|
5727
6150
|
apiUrl: auth.apiUrl
|
|
5728
6151
|
});
|
|
5729
6152
|
const fmt = createFormatter({
|
|
5730
|
-
json:
|
|
5731
|
-
quiet:
|
|
6153
|
+
json: g.json,
|
|
6154
|
+
quiet: g.quiet,
|
|
5732
6155
|
isTTY: isTTY(),
|
|
5733
6156
|
noColor: options.color === false
|
|
5734
6157
|
});
|
|
@@ -5737,7 +6160,7 @@ function askCommand() {
|
|
|
5737
6160
|
{ query: question },
|
|
5738
6161
|
ORCHESTRATOR_TIMEOUT_MS
|
|
5739
6162
|
);
|
|
5740
|
-
if (
|
|
6163
|
+
if (g.json) {
|
|
5741
6164
|
process.stdout.write(JSON.stringify(result) + "\n");
|
|
5742
6165
|
return;
|
|
5743
6166
|
}
|
|
@@ -6002,7 +6425,7 @@ if (nodeVersion < 20) {
|
|
|
6002
6425
|
console.error(" Upgrade Node.js: https://nodejs.org/");
|
|
6003
6426
|
process.exit(1);
|
|
6004
6427
|
}
|
|
6005
|
-
var VERSION = "0.1.
|
|
6428
|
+
var VERSION = "0.1.4";
|
|
6006
6429
|
var program2 = new Command().name("gad").description("Great Arrow Digital CLI \u2014 terminal access to the GAD platform").version(VERSION, "-v, --version").option("--json", "Output as JSON").option("--quiet", "Suppress decorative output").option("--no-color", "Disable color output");
|
|
6007
6430
|
program2.addCommand(login_default);
|
|
6008
6431
|
program2.addCommand(logoutCommand);
|