@great-arrow/cli 0.1.26 → 0.1.32
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 +317 -46
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -9399,7 +9399,7 @@ function isNodeError(value) {
|
|
|
9399
9399
|
|
|
9400
9400
|
// src/lib/version.ts
|
|
9401
9401
|
var CLI_PACKAGE_NAME = "@great-arrow/cli";
|
|
9402
|
-
var CLI_VERSION = "0.1.
|
|
9402
|
+
var CLI_VERSION = "0.1.32";
|
|
9403
9403
|
var GAD_INSTALL_VERSION = "2026.08.08";
|
|
9404
9404
|
var GAD_INSTALL_MIN_VERSION = "2026.06.22";
|
|
9405
9405
|
var GAD_INSTALL_HEADER = "X-GAD-Install-Version";
|
|
@@ -11741,13 +11741,30 @@ function requireGit(args, cwd) {
|
|
|
11741
11741
|
function isAncestor(ancestor, ref, cwd) {
|
|
11742
11742
|
return git(["merge-base", "--is-ancestor", ancestor, ref], cwd).status === 0;
|
|
11743
11743
|
}
|
|
11744
|
+
var AHEAD_BEHIND_FIELD = "%(ahead-behind:origin/main)";
|
|
11745
|
+
var RICH_BRANCH_FORMAT = [
|
|
11746
|
+
"%(refname:short)",
|
|
11747
|
+
"%(objectname)",
|
|
11748
|
+
"%(upstream:short)",
|
|
11749
|
+
"%(upstream:track)",
|
|
11750
|
+
"%(committerdate:iso-strict)",
|
|
11751
|
+
AHEAD_BEHIND_FIELD
|
|
11752
|
+
].join("%09");
|
|
11753
|
+
var LEGACY_BRANCH_FORMAT = [
|
|
11754
|
+
"%(refname:short)",
|
|
11755
|
+
"%(objectname)",
|
|
11756
|
+
"%(upstream:short)",
|
|
11757
|
+
"%(upstream:track)",
|
|
11758
|
+
"%(committerdate:iso-strict)"
|
|
11759
|
+
].join("%09");
|
|
11744
11760
|
function auditLocalBranches(root) {
|
|
11745
|
-
const
|
|
11746
|
-
[
|
|
11747
|
-
|
|
11748
|
-
|
|
11749
|
-
|
|
11750
|
-
|
|
11761
|
+
const rich = git(
|
|
11762
|
+
["for-each-ref", `--format=${RICH_BRANCH_FORMAT}`, "refs/heads"],
|
|
11763
|
+
root
|
|
11764
|
+
);
|
|
11765
|
+
const exact = rich.status === 0;
|
|
11766
|
+
const raw = exact ? rich.stdout.trim() : requireGit(
|
|
11767
|
+
["for-each-ref", `--format=${LEGACY_BRANCH_FORMAT}`, "refs/heads"],
|
|
11751
11768
|
root
|
|
11752
11769
|
);
|
|
11753
11770
|
const unmerged = new Set(
|
|
@@ -11762,23 +11779,32 @@ function auditLocalBranches(root) {
|
|
|
11762
11779
|
).split("\n").filter(Boolean)
|
|
11763
11780
|
);
|
|
11764
11781
|
return raw.split("\n").filter(Boolean).flatMap((line) => {
|
|
11765
|
-
const
|
|
11766
|
-
|
|
11767
|
-
const mergedToMain = !unmerged.has(branch);
|
|
11768
|
-
return [
|
|
11769
|
-
{
|
|
11770
|
-
branch,
|
|
11771
|
-
head,
|
|
11772
|
-
upstream: upstreamRaw || null,
|
|
11773
|
-
upstreamGone: tracking?.includes("gone") ?? false,
|
|
11774
|
-
aheadMain: mergedToMain ? 0 : 1,
|
|
11775
|
-
behindMain: 0,
|
|
11776
|
-
countsExact: false,
|
|
11777
|
-
mergedToMain
|
|
11778
|
-
}
|
|
11779
|
-
];
|
|
11782
|
+
const parsed = parseLocalBranchLine(line, exact, unmerged);
|
|
11783
|
+
return parsed ? [parsed] : [];
|
|
11780
11784
|
});
|
|
11781
11785
|
}
|
|
11786
|
+
function parseLocalBranchLine(line, exact, unmerged) {
|
|
11787
|
+
const [branch, head, upstreamRaw, tracking, committedAt, aheadBehind] = line.split(" ");
|
|
11788
|
+
if (!branch || !head) return null;
|
|
11789
|
+
const mergedToMain = !unmerged.has(branch);
|
|
11790
|
+
const [aheadRaw, behindRaw] = (aheadBehind ?? "").trim().split(/\s+/);
|
|
11791
|
+
const ahead = Number(aheadRaw);
|
|
11792
|
+
const behind = Number(behindRaw);
|
|
11793
|
+
const countsExact = exact && Number.isFinite(ahead) && Number.isFinite(behind);
|
|
11794
|
+
return {
|
|
11795
|
+
branch,
|
|
11796
|
+
head,
|
|
11797
|
+
upstream: upstreamRaw || null,
|
|
11798
|
+
upstreamGone: tracking?.includes("gone") ?? false,
|
|
11799
|
+
// Fall back to the old boolean ONLY when the exact count is genuinely
|
|
11800
|
+
// unavailable, so a stale git degrades instead of lying.
|
|
11801
|
+
aheadMain: countsExact ? ahead : mergedToMain ? 0 : 1,
|
|
11802
|
+
behindMain: countsExact ? behind : 0,
|
|
11803
|
+
countsExact,
|
|
11804
|
+
mergedToMain,
|
|
11805
|
+
committedAt: committedAt || null
|
|
11806
|
+
};
|
|
11807
|
+
}
|
|
11782
11808
|
function parseWorktrees(raw) {
|
|
11783
11809
|
const worktrees = [];
|
|
11784
11810
|
let current = {};
|
|
@@ -11851,13 +11877,32 @@ async function auditWorktrees(root, localBranches) {
|
|
|
11851
11877
|
}
|
|
11852
11878
|
function auditDetachedLocalBranches(localBranches, checkedOutBranches) {
|
|
11853
11879
|
return localBranches.filter(({ branch }) => !checkedOutBranches.has(branch)).map(
|
|
11854
|
-
({
|
|
11880
|
+
({
|
|
11855
11881
|
branch,
|
|
11856
11882
|
head,
|
|
11857
11883
|
mergedToMain,
|
|
11858
11884
|
behindMain,
|
|
11859
11885
|
aheadMain,
|
|
11860
|
-
countsExact
|
|
11886
|
+
countsExact,
|
|
11887
|
+
upstream,
|
|
11888
|
+
upstreamGone,
|
|
11889
|
+
committedAt
|
|
11890
|
+
}) => ({
|
|
11891
|
+
branch,
|
|
11892
|
+
head,
|
|
11893
|
+
mergedToMain,
|
|
11894
|
+
behindMain,
|
|
11895
|
+
aheadMain,
|
|
11896
|
+
countsExact,
|
|
11897
|
+
// `upstream` is what separates "never left this disk" from "pushed,
|
|
11898
|
+
// just has no PR yet" — the single bit that decides whether work can
|
|
11899
|
+
// actually be lost. It was computed and then dropped here, so the
|
|
11900
|
+
// server classified both as one undifferentiated "stranded".
|
|
11901
|
+
upstream,
|
|
11902
|
+
upstreamGone,
|
|
11903
|
+
// Without this the grace window read `ageMs = Infinity` for every
|
|
11904
|
+
// branch, so a branch was "stranded" from its first second.
|
|
11905
|
+
committedAt
|
|
11861
11906
|
})
|
|
11862
11907
|
);
|
|
11863
11908
|
}
|
|
@@ -14853,12 +14898,18 @@ function handleSyncResponse(fmt, kind, response) {
|
|
|
14853
14898
|
}
|
|
14854
14899
|
fmt.success(`Sync triggered successfully for "${kind}".`);
|
|
14855
14900
|
const syncedAtStr = formatTimestamp(
|
|
14856
|
-
response.lastSynced ?? response.last_synced
|
|
14901
|
+
response.lastSyncAt ?? response.lastSynced ?? response.last_synced
|
|
14857
14902
|
);
|
|
14858
14903
|
if (syncedAtStr) {
|
|
14859
14904
|
fmt.info(` Last synced: ${syncedAtStr}`);
|
|
14860
14905
|
}
|
|
14861
14906
|
}
|
|
14907
|
+
function normalizeIntegrationRow(item) {
|
|
14908
|
+
return {
|
|
14909
|
+
...item,
|
|
14910
|
+
lastSynced: item.lastSyncAt ?? item.lastSynced ?? item.last_synced ?? item.lastSyncedAt ?? null
|
|
14911
|
+
};
|
|
14912
|
+
}
|
|
14862
14913
|
var listCommand3 = new Command("list").description("List all connected integrations with sync status").option("--json", "Output as JSON").action(async (options) => {
|
|
14863
14914
|
const jsonFlag = Boolean(
|
|
14864
14915
|
options.json || listCommand3.optsWithGlobals().json
|
|
@@ -14878,10 +14929,7 @@ var listCommand3 = new Command("list").description("List all connected integrati
|
|
|
14878
14929
|
} else if (typeof data === "object" && data !== null && "integrations" in data) {
|
|
14879
14930
|
integrations = data.integrations;
|
|
14880
14931
|
}
|
|
14881
|
-
const normalized = integrations.map(
|
|
14882
|
-
...item,
|
|
14883
|
-
lastSynced: item.lastSynced ?? item.last_synced ?? item.lastSyncedAt ?? null
|
|
14884
|
-
}));
|
|
14932
|
+
const normalized = integrations.map(normalizeIntegrationRow);
|
|
14885
14933
|
fmt.table(normalized, integrationListColumns);
|
|
14886
14934
|
if (!jsonFlag) {
|
|
14887
14935
|
displayFailedIntegrations(fmt, normalized);
|
|
@@ -17151,14 +17199,102 @@ function localCommand() {
|
|
|
17151
17199
|
return new Command("local").description("Answer with a model running on this computer").addCommand(checkCommand()).addCommand(askHereCommand());
|
|
17152
17200
|
}
|
|
17153
17201
|
|
|
17202
|
+
// src/lib/hyperlink.ts
|
|
17203
|
+
var SUPPORTED_TERM_PROGRAMS = /* @__PURE__ */ new Set([
|
|
17204
|
+
"ghostty",
|
|
17205
|
+
"iterm.app",
|
|
17206
|
+
"wezterm",
|
|
17207
|
+
"vscode",
|
|
17208
|
+
"hyper",
|
|
17209
|
+
"warpterminal",
|
|
17210
|
+
"rio",
|
|
17211
|
+
"contour",
|
|
17212
|
+
"tabby"
|
|
17213
|
+
]);
|
|
17214
|
+
var SUPPORTED_TERMS = /* @__PURE__ */ new Set([
|
|
17215
|
+
"xterm-kitty",
|
|
17216
|
+
"alacritty",
|
|
17217
|
+
"foot",
|
|
17218
|
+
"contour"
|
|
17219
|
+
]);
|
|
17220
|
+
var UNSUPPORTED_TERM_PROGRAMS = /* @__PURE__ */ new Set(["apple_terminal"]);
|
|
17221
|
+
var MIN_VTE_VERSION = 5e3;
|
|
17222
|
+
function isTruthyFlag(value) {
|
|
17223
|
+
if (value === void 0) return false;
|
|
17224
|
+
const v = value.trim().toLowerCase();
|
|
17225
|
+
return v !== "" && v !== "0" && v !== "false";
|
|
17226
|
+
}
|
|
17227
|
+
function supportsHyperlinks(env4, isTTY2) {
|
|
17228
|
+
if (isTruthyFlag(env4.FORCE_HYPERLINK)) return true;
|
|
17229
|
+
if (env4.NO_COLOR !== void 0 && env4.NO_COLOR !== "") return false;
|
|
17230
|
+
if (!isTTY2) return false;
|
|
17231
|
+
if (isTruthyFlag(env4.CI)) return false;
|
|
17232
|
+
const term = (env4.TERM ?? "").trim().toLowerCase();
|
|
17233
|
+
if (term === "" || term === "dumb") return false;
|
|
17234
|
+
if (term.startsWith("screen") || term.startsWith("tmux")) return false;
|
|
17235
|
+
const program3 = (env4.TERM_PROGRAM ?? "").trim().toLowerCase();
|
|
17236
|
+
if (UNSUPPORTED_TERM_PROGRAMS.has(program3)) return false;
|
|
17237
|
+
if (SUPPORTED_TERM_PROGRAMS.has(program3)) return true;
|
|
17238
|
+
if (SUPPORTED_TERMS.has(term)) return true;
|
|
17239
|
+
if (isTruthyFlag(env4.WT_SESSION)) return true;
|
|
17240
|
+
if (isTruthyFlag(env4.KONSOLE_VERSION)) return true;
|
|
17241
|
+
if (env4.TERMINAL_EMULATOR === "JetBrains-JediTerm") return true;
|
|
17242
|
+
const vte = Number.parseInt(env4.VTE_VERSION ?? "", 10);
|
|
17243
|
+
if (Number.isFinite(vte) && vte >= MIN_VTE_VERSION) return true;
|
|
17244
|
+
return false;
|
|
17245
|
+
}
|
|
17246
|
+
var OSC = "\x1B]8;;";
|
|
17247
|
+
var ST = "\x1B\\";
|
|
17248
|
+
function hyperlink(text2, url, opts) {
|
|
17249
|
+
if (!opts.enabled) return text2 === url ? url : `${text2} (${url})`;
|
|
17250
|
+
return `${OSC}${url}${ST}${text2}${OSC}${ST}`;
|
|
17251
|
+
}
|
|
17252
|
+
|
|
17154
17253
|
// src/lib/glance-footer.ts
|
|
17155
17254
|
import { readFile as readFile4, writeFile as writeFile4, mkdir as mkdir4 } from "node:fs/promises";
|
|
17156
17255
|
import { homedir as homedir4 } from "node:os";
|
|
17157
17256
|
import { join as join6, dirname as dirname5 } from "node:path";
|
|
17257
|
+
|
|
17258
|
+
// src/lib/seen.ts
|
|
17259
|
+
var SEEN_TIMEOUT_MS = 1e3;
|
|
17260
|
+
var MAX_KEYS = 50;
|
|
17261
|
+
async function reportSeen(opts) {
|
|
17262
|
+
const keys = [
|
|
17263
|
+
...new Set(
|
|
17264
|
+
opts.keys.filter((k) => typeof k === "string" && k !== "")
|
|
17265
|
+
)
|
|
17266
|
+
].slice(0, MAX_KEYS);
|
|
17267
|
+
if (keys.length === 0) return 0;
|
|
17268
|
+
const controller = new AbortController();
|
|
17269
|
+
const timer = setTimeout(() => controller.abort(), SEEN_TIMEOUT_MS);
|
|
17270
|
+
try {
|
|
17271
|
+
const response = await fetch(
|
|
17272
|
+
`${opts.apiUrl.replace(/\/$/, "")}/api/attention/seen`,
|
|
17273
|
+
{
|
|
17274
|
+
method: "POST",
|
|
17275
|
+
headers: {
|
|
17276
|
+
"Content-Type": "application/json",
|
|
17277
|
+
Authorization: `Bearer ${opts.token}`
|
|
17278
|
+
},
|
|
17279
|
+
body: JSON.stringify({ keys }),
|
|
17280
|
+
signal: controller.signal
|
|
17281
|
+
}
|
|
17282
|
+
);
|
|
17283
|
+
if (!response.ok) return 0;
|
|
17284
|
+
const data = await response.json().catch(() => null);
|
|
17285
|
+
return typeof data?.seen === "number" ? data.seen : 0;
|
|
17286
|
+
} catch {
|
|
17287
|
+
return 0;
|
|
17288
|
+
} finally {
|
|
17289
|
+
clearTimeout(timer);
|
|
17290
|
+
}
|
|
17291
|
+
}
|
|
17292
|
+
|
|
17293
|
+
// src/lib/glance-footer.ts
|
|
17158
17294
|
var FETCH_TIMEOUT_MS = 1500;
|
|
17159
17295
|
var THROTTLE_INTERVAL_MS = 4 * 60 * 60 * 1e3;
|
|
17160
17296
|
var URGENT_THRESHOLD = 15;
|
|
17161
|
-
var SKIP_COMMANDS = /* @__PURE__ */ new Set(["status", "login", "logout", "help"]);
|
|
17297
|
+
var SKIP_COMMANDS = /* @__PURE__ */ new Set(["status", "needs", "login", "logout", "help"]);
|
|
17162
17298
|
async function maybeShowGlanceFooter(argv) {
|
|
17163
17299
|
try {
|
|
17164
17300
|
if (!shouldRunGlanceFooter(argv)) return;
|
|
@@ -17178,9 +17314,14 @@ async function maybeShowGlanceFooter(argv) {
|
|
|
17178
17314
|
})) {
|
|
17179
17315
|
return;
|
|
17180
17316
|
}
|
|
17181
|
-
renderFooter(payload, auth.apiUrl);
|
|
17317
|
+
const printed = renderFooter(payload, auth.apiUrl);
|
|
17182
17318
|
state[workspaceKey] = { lastShownAt: now, lastTopItemId: signature };
|
|
17183
17319
|
await saveGlanceState(state);
|
|
17320
|
+
await reportSeen({
|
|
17321
|
+
apiUrl: auth.apiUrl,
|
|
17322
|
+
token: auth.token,
|
|
17323
|
+
keys: printed
|
|
17324
|
+
});
|
|
17184
17325
|
} catch (err) {
|
|
17185
17326
|
debugSwallow("glance-footer", err);
|
|
17186
17327
|
}
|
|
@@ -17247,7 +17388,28 @@ function normalizeGlance(raw) {
|
|
|
17247
17388
|
title: it.title,
|
|
17248
17389
|
action: typeof it.action === "string" ? it.action : null,
|
|
17249
17390
|
href: typeof it.href === "string" && it.href !== "" ? it.href : null,
|
|
17250
|
-
...typeof it.rank === "number" ? { rank: it.rank } : {}
|
|
17391
|
+
...typeof it.rank === "number" ? { rank: it.rank } : {},
|
|
17392
|
+
signalKey: typeof it.signalKey === "string" && it.signalKey !== "" ? it.signalKey : null,
|
|
17393
|
+
workspace: typeof it.workspace === "string" && it.workspace !== "" ? it.workspace : null,
|
|
17394
|
+
// Parsed like every other field: a non-string is dropped rather than
|
|
17395
|
+
// coerced, so a malformed payload cannot render as a currency claim.
|
|
17396
|
+
freshness: typeof it.freshness === "string" && it.freshness !== "" ? it.freshness : null,
|
|
17397
|
+
meta: typeof it.meta === "string" && it.meta !== "" ? it.meta : null
|
|
17398
|
+
}
|
|
17399
|
+
];
|
|
17400
|
+
});
|
|
17401
|
+
const atRisk = asArray2(obj.atRisk).flatMap((item) => {
|
|
17402
|
+
if (typeof item !== "object" || item === null) return [];
|
|
17403
|
+
const it = item;
|
|
17404
|
+
if (typeof it.title !== "string") return [];
|
|
17405
|
+
return [
|
|
17406
|
+
{
|
|
17407
|
+
classification: typeof it.classification === "string" ? it.classification : null,
|
|
17408
|
+
riskLevel: typeof it.riskLevel === "string" ? it.riskLevel : "high",
|
|
17409
|
+
title: it.title,
|
|
17410
|
+
branch: typeof it.branch === "string" ? it.branch : null,
|
|
17411
|
+
aheadMain: typeof it.aheadMain === "number" ? it.aheadMain : null,
|
|
17412
|
+
recommendedAction: typeof it.recommendedAction === "string" ? it.recommendedAction : null
|
|
17251
17413
|
}
|
|
17252
17414
|
];
|
|
17253
17415
|
});
|
|
@@ -17301,11 +17463,29 @@ function normalizeGlance(raw) {
|
|
|
17301
17463
|
}
|
|
17302
17464
|
return {
|
|
17303
17465
|
workspace: typeof obj.workspace === "string" && obj.workspace !== "" ? obj.workspace : null,
|
|
17466
|
+
// An older server sends no `reads` at all, so this normalizes to an empty
|
|
17467
|
+
// list rather than inventing the pinned workspace as the read set — the
|
|
17468
|
+
// CLI then renders no breadth cue, which is honest, instead of a claim it
|
|
17469
|
+
// was never told.
|
|
17470
|
+
reads: Array.isArray(obj.reads) ? obj.reads.flatMap((r) => {
|
|
17471
|
+
if (typeof r !== "object" || r === null) return [];
|
|
17472
|
+
const row = r;
|
|
17473
|
+
return typeof row.id === "string" && typeof row.name === "string" ? [{ id: row.id, name: row.name }] : [];
|
|
17474
|
+
}) : [],
|
|
17304
17475
|
needsYou,
|
|
17476
|
+
atRisk,
|
|
17305
17477
|
topGoal,
|
|
17306
17478
|
integrations,
|
|
17307
17479
|
agents,
|
|
17308
17480
|
warRoom,
|
|
17481
|
+
// Parsed like every other field: both halves must be the right type or the
|
|
17482
|
+
// whole line is dropped, so a malformed payload cannot render as a dollar.
|
|
17483
|
+
spend: (() => {
|
|
17484
|
+
const raw2 = obj.spend;
|
|
17485
|
+
if (typeof raw2 !== "object" || raw2 === null) return null;
|
|
17486
|
+
const sp = raw2;
|
|
17487
|
+
return typeof sp.line === "string" && sp.line !== "" && typeof sp.totalCostUsd === "number" && Number.isFinite(sp.totalCostUsd) ? { line: sp.line, totalCostUsd: sp.totalCostUsd } : null;
|
|
17488
|
+
})(),
|
|
17309
17489
|
urgency: typeof obj.urgency === "number" ? obj.urgency : null,
|
|
17310
17490
|
generatedAt: typeof obj.generatedAt === "string" ? obj.generatedAt : (/* @__PURE__ */ new Date()).toISOString()
|
|
17311
17491
|
};
|
|
@@ -17362,14 +17542,33 @@ function glanceItemUrl(href, apiUrl) {
|
|
|
17362
17542
|
function renderFooter(payload, apiUrl) {
|
|
17363
17543
|
const c = createColor(false);
|
|
17364
17544
|
const lines = [];
|
|
17545
|
+
const printed = [];
|
|
17365
17546
|
const wr = payload.warRoom;
|
|
17366
17547
|
if (wr && wr.priorityCount > 0) {
|
|
17367
17548
|
lines.push(
|
|
17368
17549
|
`${c.yellow("!")} ${wr.priorityCount} war-room message${wr.priorityCount === 1 ? "" : "s"} for you${wr.newestSender ? ` \u2014 ${wr.newestSender}` : ""}`
|
|
17369
17550
|
);
|
|
17370
17551
|
}
|
|
17552
|
+
const unpushed = (payload.atRisk ?? []).filter(
|
|
17553
|
+
(item) => item.classification === "unpushed"
|
|
17554
|
+
);
|
|
17555
|
+
if (unpushed.length > 0) {
|
|
17556
|
+
const commits = unpushed.reduce(
|
|
17557
|
+
(sum, item) => sum + (item.aheadMain ?? 0),
|
|
17558
|
+
0
|
|
17559
|
+
);
|
|
17560
|
+
const scale = commits > 0 ? `${commits} commit${commits === 1 ? "" : "s"} on ${unpushed.length} branch${unpushed.length === 1 ? "" : "es"}` : `${unpushed.length} branch${unpushed.length === 1 ? "" : "es"}`;
|
|
17561
|
+
lines.push(`${c.yellow("!")} ${scale} exist on this machine only`);
|
|
17562
|
+
const first = unpushed[0];
|
|
17563
|
+
if (first?.branch) {
|
|
17564
|
+
lines.push(
|
|
17565
|
+
` ${c.dim("\u2192")} ${c.cyan(`git push -u origin ${first.branch}`)}`
|
|
17566
|
+
);
|
|
17567
|
+
}
|
|
17568
|
+
}
|
|
17371
17569
|
for (const item of payload.needsYou.slice(0, 2)) {
|
|
17372
17570
|
lines.push(`${c.yellow("!")} ${item.title}`);
|
|
17571
|
+
if (item.signalKey) printed.push(item.signalKey);
|
|
17373
17572
|
if (item.action) {
|
|
17374
17573
|
lines.push(` ${c.dim("\u2192")} ${c.cyan(item.action)}`);
|
|
17375
17574
|
} else {
|
|
@@ -17387,21 +17586,21 @@ function renderFooter(payload, apiUrl) {
|
|
|
17387
17586
|
}
|
|
17388
17587
|
}
|
|
17389
17588
|
}
|
|
17390
|
-
if (lines.length === 0) return;
|
|
17589
|
+
if (lines.length === 0) return [];
|
|
17391
17590
|
lines.push(`${c.dim("\u2192")} ${c.cyan("gad status")} for the full picture.`);
|
|
17392
17591
|
process.stderr.write("\n" + brandedBlock("needs you", lines) + "\n");
|
|
17592
|
+
return printed;
|
|
17393
17593
|
}
|
|
17394
|
-
var
|
|
17594
|
+
var GAD_MARK = "\u{1F3F9}";
|
|
17395
17595
|
function brandedBlock(title, lines) {
|
|
17396
|
-
const header = `${
|
|
17397
|
-
const
|
|
17398
|
-
|
|
17399
|
-
return [header, rule, ...lines].join("\\n");
|
|
17596
|
+
const header = `${GAD_MARK} Great Arrow \xB7 ${title}`;
|
|
17597
|
+
const rule = "\u2500".repeat(Math.min(60, [...header].length));
|
|
17598
|
+
return [header, rule, ...lines].join("\n");
|
|
17400
17599
|
}
|
|
17401
17600
|
|
|
17402
17601
|
// src/commands/status.ts
|
|
17403
17602
|
function statusCommand2() {
|
|
17404
|
-
const cmd = new Command("status").description(
|
|
17603
|
+
const cmd = new Command("status").alias("needs").description(
|
|
17405
17604
|
"Show what needs you: blockers, Goal progress, stale data, agents"
|
|
17406
17605
|
).option("--json", "Output the raw glance payload as JSON").option("--quiet", "Suppress decorative output").option("--no-color", "Disable color output").action(async (options, command) => {
|
|
17407
17606
|
const g = globalOutputOpts(command);
|
|
@@ -17437,11 +17636,18 @@ function statusCommand2() {
|
|
|
17437
17636
|
return;
|
|
17438
17637
|
}
|
|
17439
17638
|
renderStatus(payload, g.noColor || g.quiet, auth.apiUrl);
|
|
17639
|
+
await reportSeen({
|
|
17640
|
+
apiUrl: auth.apiUrl,
|
|
17641
|
+
token: auth.token,
|
|
17642
|
+
keys: payload.needsYou.map((item) => item.signalKey)
|
|
17643
|
+
});
|
|
17440
17644
|
});
|
|
17441
17645
|
return cmd;
|
|
17442
17646
|
}
|
|
17443
17647
|
function renderStatus(payload, plain, apiUrl) {
|
|
17444
17648
|
const c = createColor(plain);
|
|
17649
|
+
const links = supportsHyperlinks(process.env, isTTY());
|
|
17650
|
+
const link = (text2, url) => c.cyan(hyperlink(text2, url, { enabled: links }));
|
|
17445
17651
|
const lines = [];
|
|
17446
17652
|
if (payload.workspace) {
|
|
17447
17653
|
lines.push(c.badge(` ${payload.workspace} `));
|
|
@@ -17464,18 +17670,23 @@ function renderStatus(payload, plain, apiUrl) {
|
|
|
17464
17670
|
if (wr.newestPreview) {
|
|
17465
17671
|
lines.push(` ${c.dim(wr.newestPreview)}`);
|
|
17466
17672
|
}
|
|
17467
|
-
|
|
17468
|
-
|
|
17469
|
-
|
|
17673
|
+
const warRoomUrl = glanceItemUrl("/team?tab=war-room", apiUrl);
|
|
17674
|
+
if (warRoomUrl) {
|
|
17675
|
+
lines.push(
|
|
17676
|
+
` ${c.dim("\u2192")} ${link("Open the war room", warRoomUrl)}`
|
|
17677
|
+
);
|
|
17678
|
+
}
|
|
17470
17679
|
lines.push("");
|
|
17471
17680
|
}
|
|
17472
17681
|
if (payload.needsYou.length > 0) {
|
|
17473
17682
|
lines.push(c.bold("Needs you"));
|
|
17474
17683
|
for (const item of payload.needsYou) {
|
|
17475
|
-
|
|
17684
|
+
const from = item.workspace ? ` ${c.dim(`(${item.workspace})`)}` : "";
|
|
17685
|
+
lines.push(` ${c.yellow("!")} ${item.title}${from}`);
|
|
17686
|
+
if (item.freshness) lines.push(` ${c.dim(item.freshness)}`);
|
|
17476
17687
|
if (item.action) lines.push(` ${c.dim("\u2192")} ${c.cyan(item.action)}`);
|
|
17477
17688
|
const url = glanceItemUrl(item.href, apiUrl);
|
|
17478
|
-
if (url) lines.push(` ${c.dim("\u2192")} ${
|
|
17689
|
+
if (url) lines.push(` ${c.dim("\u2192")} ${link(item.title, url)}`);
|
|
17479
17690
|
}
|
|
17480
17691
|
lines.push("");
|
|
17481
17692
|
}
|
|
@@ -17678,6 +17889,65 @@ function nextCommand() {
|
|
|
17678
17889
|
});
|
|
17679
17890
|
}
|
|
17680
17891
|
|
|
17892
|
+
// src/commands/discover.ts
|
|
17893
|
+
var DISCOVER_COLUMNS = [
|
|
17894
|
+
{ key: "category", header: "Area", width: 20 },
|
|
17895
|
+
{ key: "text", header: "Did you know", width: 78 },
|
|
17896
|
+
{ key: "url", header: "Open" }
|
|
17897
|
+
];
|
|
17898
|
+
var AREA_LABELS = {
|
|
17899
|
+
feature_discovery: "Capability",
|
|
17900
|
+
workflow: "Workflow",
|
|
17901
|
+
integration_health: "Connections",
|
|
17902
|
+
memory: "Memory",
|
|
17903
|
+
meetings: "Meetings",
|
|
17904
|
+
models: "Models",
|
|
17905
|
+
opportunity: "Setup"
|
|
17906
|
+
};
|
|
17907
|
+
function discoverRow(item) {
|
|
17908
|
+
const category = typeof item.category === "string" ? item.category : "";
|
|
17909
|
+
return {
|
|
17910
|
+
category: AREA_LABELS[category] ?? category,
|
|
17911
|
+
text: typeof item.text === "string" ? item.text : "",
|
|
17912
|
+
url: typeof item.url === "string" ? item.url : ""
|
|
17913
|
+
};
|
|
17914
|
+
}
|
|
17915
|
+
function discoverCommand() {
|
|
17916
|
+
return new Command("discover").description("Show what Great Arrow can do that you're not using yet").option("--limit <n>", "How many suggestions to show").option("--json", "Output as JSON").action(async (opts, command) => {
|
|
17917
|
+
const g = globalOutputOpts(command);
|
|
17918
|
+
const fmt = createFormatter({
|
|
17919
|
+
json: g.json,
|
|
17920
|
+
quiet: g.quiet,
|
|
17921
|
+
isTTY: isTTY(),
|
|
17922
|
+
noColor: g.noColor
|
|
17923
|
+
});
|
|
17924
|
+
const auth = await resolveAuth();
|
|
17925
|
+
const client = new GadClient({ token: auth.token, apiUrl: auth.apiUrl });
|
|
17926
|
+
const parsed = opts.limit ? Number.parseInt(opts.limit, 10) : NaN;
|
|
17927
|
+
const args = Number.isFinite(parsed) && parsed > 0 ? { limit: parsed } : {};
|
|
17928
|
+
const result = await client.mcpCall("did_you_know", args);
|
|
17929
|
+
const items = Array.isArray(result?.items) ? result.items : [];
|
|
17930
|
+
if (items.length === 0) {
|
|
17931
|
+
if (!g.json) {
|
|
17932
|
+
fmt.info(
|
|
17933
|
+
"Nothing new to suggest \u2014 this workspace already uses what its connected tools support. Connect another tool with 'gad integrations' to unlock more."
|
|
17934
|
+
);
|
|
17935
|
+
} else {
|
|
17936
|
+
fmt.table([], DISCOVER_COLUMNS);
|
|
17937
|
+
}
|
|
17938
|
+
return;
|
|
17939
|
+
}
|
|
17940
|
+
fmt.table(items.map(discoverRow), DISCOVER_COLUMNS);
|
|
17941
|
+
if (!g.json) {
|
|
17942
|
+
const using = Number(result?.alreadyUsingCount ?? 0);
|
|
17943
|
+
const total = Number(result?.totalCount ?? 0);
|
|
17944
|
+
if (Number.isFinite(using) && Number.isFinite(total) && total > 0) {
|
|
17945
|
+
fmt.info(`You're already using ${using} of ${total} capabilities.`);
|
|
17946
|
+
}
|
|
17947
|
+
}
|
|
17948
|
+
});
|
|
17949
|
+
}
|
|
17950
|
+
|
|
17681
17951
|
// src/lib/completions.ts
|
|
17682
17952
|
var COMMANDS = [
|
|
17683
17953
|
"login",
|
|
@@ -20187,8 +20457,8 @@ import process10 from "node:process";
|
|
|
20187
20457
|
|
|
20188
20458
|
// node_modules/.pnpm/ansi-regex@6.2.2/node_modules/ansi-regex/index.js
|
|
20189
20459
|
function ansiRegex({ onlyFirst = false } = {}) {
|
|
20190
|
-
const
|
|
20191
|
-
const osc = `(?:\\u001B\\][\\s\\S]*?${
|
|
20460
|
+
const ST2 = "(?:\\u0007|\\u001B\\u005C|\\u009C)";
|
|
20461
|
+
const osc = `(?:\\u001B\\][\\s\\S]*?${ST2})`;
|
|
20192
20462
|
const csi = "[\\u001B\\u009B][[\\]()#;?]*(?:\\d{1,4}(?:[;:]\\d{0,4})*)?[\\dA-PR-TZcf-nq-uy=><~]";
|
|
20193
20463
|
const pattern = `${osc}|${csi}`;
|
|
20194
20464
|
return new RegExp(pattern, onlyFirst ? void 0 : "g");
|
|
@@ -21318,6 +21588,7 @@ program2.addCommand(memoryCommand);
|
|
|
21318
21588
|
program2.addCommand(workspace_default);
|
|
21319
21589
|
program2.addCommand(todo_default);
|
|
21320
21590
|
program2.addCommand(nextCommand());
|
|
21591
|
+
program2.addCommand(discoverCommand());
|
|
21321
21592
|
program2.addCommand(claim_default);
|
|
21322
21593
|
program2.addCommand(work_default());
|
|
21323
21594
|
program2.addCommand(worktree_default);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@great-arrow/cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.32",
|
|
4
4
|
"description": "GreatArrow.ai CLI tool",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
"license": "SEE LICENSE IN LICENSE",
|
|
39
39
|
"repository": {
|
|
40
40
|
"type": "git",
|
|
41
|
-
"url": "git+https://github.com/
|
|
41
|
+
"url": "git+https://github.com/GreatArrow-ai/great-arrow-digital.git",
|
|
42
42
|
"directory": "packages/cli"
|
|
43
43
|
},
|
|
44
44
|
"homepage": "https://www.greatarrow.ai",
|