githits 0.4.5 → 0.4.7
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/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/.plugin/plugin.json +1 -1
- package/GEMINI.md +2 -1
- package/README.md +5 -4
- package/commands/help.md +2 -1
- package/dist/cli.js +1427 -360
- package/dist/index.js +1 -1
- package/dist/shared/{chunk-zzmbjttb.js → chunk-70bn2pmx.js} +2 -2
- package/dist/shared/{chunk-vfyz65v1.js → chunk-7b4f7fd5.js} +1 -1
- package/dist/shared/{chunk-ygqss6gp.js → chunk-tm11qwgr.js} +416 -78
- package/gemini-extension.json +1 -1
- package/package.json +11 -11
- package/plugins/claude/.claude-plugin/plugin.json +1 -1
- package/plugins/claude/commands/help.md +2 -1
- package/skills/githits-code/SKILL.md +1 -1
- package/skills/githits-code/references/code-and-docs.md +2 -2
package/dist/cli.js
CHANGED
|
@@ -51,11 +51,11 @@ import {
|
|
|
51
51
|
shouldRunUpdateCheck,
|
|
52
52
|
startTelemetrySpan,
|
|
53
53
|
withTelemetrySpan
|
|
54
|
-
} from "./shared/chunk-
|
|
54
|
+
} from "./shared/chunk-tm11qwgr.js";
|
|
55
55
|
import {
|
|
56
56
|
__require,
|
|
57
57
|
version
|
|
58
|
-
} from "./shared/chunk-
|
|
58
|
+
} from "./shared/chunk-7b4f7fd5.js";
|
|
59
59
|
|
|
60
60
|
// src/cli.ts
|
|
61
61
|
import { Command } from "commander";
|
|
@@ -331,6 +331,7 @@ var colors = {
|
|
|
331
331
|
reset: "\x1B[0m",
|
|
332
332
|
bold: "\x1B[1m",
|
|
333
333
|
dim: "\x1B[2m",
|
|
334
|
+
italic: "\x1B[3m",
|
|
334
335
|
green: "\x1B[32m",
|
|
335
336
|
yellow: "\x1B[33m",
|
|
336
337
|
blue: "\x1B[34m",
|
|
@@ -338,6 +339,20 @@ var colors = {
|
|
|
338
339
|
cyan: "\x1B[36m",
|
|
339
340
|
red: "\x1B[31m"
|
|
340
341
|
};
|
|
342
|
+
var brandColors = {
|
|
343
|
+
primary: {
|
|
344
|
+
hex: "#FF72BE",
|
|
345
|
+
rgb: [255, 114, 190],
|
|
346
|
+
ansi256: 205,
|
|
347
|
+
ansi16: "magenta"
|
|
348
|
+
},
|
|
349
|
+
secondary: {
|
|
350
|
+
hex: "#FF872F",
|
|
351
|
+
rgb: [255, 135, 47],
|
|
352
|
+
ansi256: 208,
|
|
353
|
+
ansi16: "yellow"
|
|
354
|
+
}
|
|
355
|
+
};
|
|
341
356
|
function shouldUseColors(noColor) {
|
|
342
357
|
if (noColor)
|
|
343
358
|
return false;
|
|
@@ -350,6 +365,32 @@ function colorize(text, color, useColors) {
|
|
|
350
365
|
return text;
|
|
351
366
|
return `${colors[color]}${text}${colors.reset}`;
|
|
352
367
|
}
|
|
368
|
+
function getColorDepth() {
|
|
369
|
+
const stream = process.stdout;
|
|
370
|
+
return stream.getColorDepth?.() ?? 1;
|
|
371
|
+
}
|
|
372
|
+
function foregroundColorCode(color, colorDepth) {
|
|
373
|
+
if (colorDepth >= 24) {
|
|
374
|
+
const [red, green, blue] = color.rgb;
|
|
375
|
+
return `\x1B[38;2;${red};${green};${blue}m`;
|
|
376
|
+
}
|
|
377
|
+
if (colorDepth >= 8) {
|
|
378
|
+
return `\x1B[38;5;${color.ansi256}m`;
|
|
379
|
+
}
|
|
380
|
+
return colors[color.ansi16];
|
|
381
|
+
}
|
|
382
|
+
function colorizeTerminal(text, color, useColors, options = {}) {
|
|
383
|
+
if (!useColors)
|
|
384
|
+
return text;
|
|
385
|
+
const colorDepth = options.colorDepth ?? getColorDepth();
|
|
386
|
+
if (colorDepth <= 1)
|
|
387
|
+
return text;
|
|
388
|
+
const prefix = `${options.bold ? colors.bold : ""}${options.dim ? colors.dim : ""}${foregroundColorCode(color, colorDepth)}`;
|
|
389
|
+
return `${prefix}${text}${colors.reset}`;
|
|
390
|
+
}
|
|
391
|
+
function colorizeBrand(text, colorName, useColors, options) {
|
|
392
|
+
return colorizeTerminal(text, brandColors[colorName], useColors, options);
|
|
393
|
+
}
|
|
353
394
|
function success(text, useColors) {
|
|
354
395
|
const checkmark = useColors ? `${colors.green}✓${colors.reset}` : "✓";
|
|
355
396
|
return `${checkmark} ${text}`;
|
|
@@ -583,6 +624,12 @@ function classify(error2) {
|
|
|
583
624
|
if (error2.availableVersions && error2.availableVersions.length > 0) {
|
|
584
625
|
details.availableVersions = error2.availableVersions;
|
|
585
626
|
}
|
|
627
|
+
if (error2.availableRefs && error2.availableRefs.length > 0) {
|
|
628
|
+
details.availableRefs = error2.availableRefs;
|
|
629
|
+
}
|
|
630
|
+
if (error2.targetResolution) {
|
|
631
|
+
details.targetResolution = error2.targetResolution;
|
|
632
|
+
}
|
|
586
633
|
return {
|
|
587
634
|
code: "INDEXING",
|
|
588
635
|
message: error2.message,
|
|
@@ -788,7 +835,7 @@ function parseCodeNavigationTargetSpec(spec) {
|
|
|
788
835
|
function parseRepoTarget(spec) {
|
|
789
836
|
const hashIndex = spec.lastIndexOf("#");
|
|
790
837
|
if (hashIndex === -1) {
|
|
791
|
-
|
|
838
|
+
return { repoUrl: spec };
|
|
792
839
|
}
|
|
793
840
|
const repoUrl = spec.slice(0, hashIndex);
|
|
794
841
|
const gitRef = spec.slice(hashIndex + 1);
|
|
@@ -827,6 +874,7 @@ function buildSearchHitFollowUpCommand(hit) {
|
|
|
827
874
|
version: loc.version,
|
|
828
875
|
repoUrl: loc.repoUrl,
|
|
829
876
|
gitRef: loc.gitRef,
|
|
877
|
+
requestedRef: loc.requestedRef,
|
|
830
878
|
filePath: loc.filePath,
|
|
831
879
|
startLine: loc.startLine,
|
|
832
880
|
endLine: loc.endLine,
|
|
@@ -863,9 +911,8 @@ function buildTargetSpec(input) {
|
|
|
863
911
|
return `${input.registry}:${input.packageName}${input.version ? `@${input.version}` : ""}`;
|
|
864
912
|
}
|
|
865
913
|
if (input.repoUrl) {
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
return `${input.repoUrl}#${input.gitRef}`;
|
|
914
|
+
const ref = input.gitRef ?? input.requestedRef;
|
|
915
|
+
return ref ? `${input.repoUrl}#${ref}` : input.repoUrl;
|
|
869
916
|
}
|
|
870
917
|
if (input.registry && input.packageName) {
|
|
871
918
|
return `${input.registry}:${input.packageName}${input.version ? `@${input.version}` : ""}`;
|
|
@@ -1061,6 +1108,161 @@ function shellQuote(value) {
|
|
|
1061
1108
|
return `'${value.replaceAll("'", `'"'"'`)}'`;
|
|
1062
1109
|
}
|
|
1063
1110
|
|
|
1111
|
+
// src/shared/target-resolution.ts
|
|
1112
|
+
function projectTargetResolution(resolution) {
|
|
1113
|
+
if (!resolution)
|
|
1114
|
+
return;
|
|
1115
|
+
return {
|
|
1116
|
+
...resolution.requested ? { requested: projectIdentity(resolution.requested) } : {},
|
|
1117
|
+
...resolution.resolvedRequested ? { resolvedRequested: projectIdentity(resolution.resolvedRequested) } : {},
|
|
1118
|
+
...resolution.served ? { served: projectIdentity(resolution.served) } : {},
|
|
1119
|
+
...resolution.freshness ? { freshness: resolution.freshness } : {},
|
|
1120
|
+
...resolution.freshnessReason ? { freshnessReason: resolution.freshnessReason } : {},
|
|
1121
|
+
...resolution.indexingRef ? { indexingRef: resolution.indexingRef } : {},
|
|
1122
|
+
availableVersions: resolution.availableVersions.map(projectArtifact),
|
|
1123
|
+
availableRefs: resolution.availableRefs.map(projectArtifact)
|
|
1124
|
+
};
|
|
1125
|
+
}
|
|
1126
|
+
function buildTargetResolutionNotes(resolution) {
|
|
1127
|
+
if (!resolution)
|
|
1128
|
+
return [];
|
|
1129
|
+
const lines = [];
|
|
1130
|
+
const requested = formatIdentity(resolution.requested);
|
|
1131
|
+
const fresh = formatIdentity(resolution.resolvedRequested);
|
|
1132
|
+
const served = formatIdentity(resolution.served);
|
|
1133
|
+
const reason = resolution.freshnessReason ? ` (${resolution.freshnessReason})` : "";
|
|
1134
|
+
switch (resolution.freshness) {
|
|
1135
|
+
case "fallback_recent": {
|
|
1136
|
+
const parts = ["using recent index"];
|
|
1137
|
+
if (served)
|
|
1138
|
+
parts.push(`served=${served}`);
|
|
1139
|
+
if (fresh && identitiesMateriallyDiffer(fresh, served)) {
|
|
1140
|
+
parts.push(`fresh=${fresh}`);
|
|
1141
|
+
}
|
|
1142
|
+
lines.push(`${parts.join(" | ")}${reason}`);
|
|
1143
|
+
break;
|
|
1144
|
+
}
|
|
1145
|
+
case "indexing": {
|
|
1146
|
+
const parts = ["indexing fresh target"];
|
|
1147
|
+
if (requested)
|
|
1148
|
+
parts.push(`requested=${requested}`);
|
|
1149
|
+
if (fresh)
|
|
1150
|
+
parts.push(`fresh=${fresh}`);
|
|
1151
|
+
if (resolution.indexingRef)
|
|
1152
|
+
parts.push(`indexingRef=${resolution.indexingRef}`);
|
|
1153
|
+
lines.push(`${parts.join(" | ")}${reason}`);
|
|
1154
|
+
break;
|
|
1155
|
+
}
|
|
1156
|
+
case "unavailable": {
|
|
1157
|
+
const parts = ["target unavailable"];
|
|
1158
|
+
if (requested)
|
|
1159
|
+
parts.push(`requested=${requested}`);
|
|
1160
|
+
lines.push(`${parts.join(" | ")}${reason}`);
|
|
1161
|
+
break;
|
|
1162
|
+
}
|
|
1163
|
+
case "current": {
|
|
1164
|
+
break;
|
|
1165
|
+
}
|
|
1166
|
+
default: {
|
|
1167
|
+
if (resolution.freshness || identitiesDiffer(requested, fresh, served)) {
|
|
1168
|
+
const parts = [
|
|
1169
|
+
`target resolution: ${resolution.freshness ?? "unknown"}`
|
|
1170
|
+
];
|
|
1171
|
+
if (served)
|
|
1172
|
+
parts.push(`served=${served}`);
|
|
1173
|
+
if (requested)
|
|
1174
|
+
parts.push(`requested=${requested}`);
|
|
1175
|
+
if (fresh && fresh !== served)
|
|
1176
|
+
parts.push(`fresh=${fresh}`);
|
|
1177
|
+
lines.push(`${parts.join(" | ")}${reason}`);
|
|
1178
|
+
}
|
|
1179
|
+
break;
|
|
1180
|
+
}
|
|
1181
|
+
}
|
|
1182
|
+
const candidates = buildRetryCandidateLine(resolution);
|
|
1183
|
+
if (candidates)
|
|
1184
|
+
lines.push(candidates);
|
|
1185
|
+
return lines;
|
|
1186
|
+
}
|
|
1187
|
+
function buildRetryCandidateLine(resolution) {
|
|
1188
|
+
if (!resolution)
|
|
1189
|
+
return;
|
|
1190
|
+
const parts = [];
|
|
1191
|
+
if (resolution.availableVersions.length > 0) {
|
|
1192
|
+
parts.push(`versions=${resolution.availableVersions.map(formatArtifact).join(",")}`);
|
|
1193
|
+
}
|
|
1194
|
+
if (resolution.availableRefs.length > 0) {
|
|
1195
|
+
parts.push(`refs=${resolution.availableRefs.map(formatArtifact).join(",")}`);
|
|
1196
|
+
}
|
|
1197
|
+
return parts.length > 0 ? `queryable now: ${parts.join(" | ")}` : undefined;
|
|
1198
|
+
}
|
|
1199
|
+
function buildResolutionFromRetryCandidates(target) {
|
|
1200
|
+
if (!target.availableVersions?.length && !target.availableRefs?.length) {
|
|
1201
|
+
return;
|
|
1202
|
+
}
|
|
1203
|
+
return {
|
|
1204
|
+
freshness: target.freshness,
|
|
1205
|
+
indexingRef: target.indexingRef,
|
|
1206
|
+
availableVersions: target.availableVersions ?? [],
|
|
1207
|
+
availableRefs: target.availableRefs ?? []
|
|
1208
|
+
};
|
|
1209
|
+
}
|
|
1210
|
+
function projectIdentity(identity) {
|
|
1211
|
+
const out = {};
|
|
1212
|
+
if (identity.kind)
|
|
1213
|
+
out.kind = identity.kind;
|
|
1214
|
+
if (identity.registry)
|
|
1215
|
+
out.registry = identity.registry;
|
|
1216
|
+
if (identity.packageName)
|
|
1217
|
+
out.packageName = identity.packageName;
|
|
1218
|
+
if (identity.version)
|
|
1219
|
+
out.version = identity.version;
|
|
1220
|
+
if (identity.repoUrl)
|
|
1221
|
+
out.repoUrl = identity.repoUrl;
|
|
1222
|
+
if (identity.gitRef)
|
|
1223
|
+
out.gitRef = identity.gitRef;
|
|
1224
|
+
if (identity.commitSha)
|
|
1225
|
+
out.commitSha = identity.commitSha;
|
|
1226
|
+
return out;
|
|
1227
|
+
}
|
|
1228
|
+
function projectArtifact(artifact) {
|
|
1229
|
+
return artifact.version ? { version: artifact.version, ref: artifact.ref } : { ref: artifact.ref };
|
|
1230
|
+
}
|
|
1231
|
+
function formatIdentity(identity) {
|
|
1232
|
+
if (!identity)
|
|
1233
|
+
return;
|
|
1234
|
+
if (identity.registry && identity.packageName) {
|
|
1235
|
+
const version2 = identity.version ? `@${identity.version}` : "";
|
|
1236
|
+
const commit = identity.commitSha ? `#${shortSha(identity.commitSha)}` : "";
|
|
1237
|
+
return `${identity.registry.toLowerCase()}:${identity.packageName}${version2}${commit}`;
|
|
1238
|
+
}
|
|
1239
|
+
if (identity.repoUrl) {
|
|
1240
|
+
const ref = identity.gitRef ? `#${identity.gitRef}` : "";
|
|
1241
|
+
const commit = identity.commitSha ? `@${shortSha(identity.commitSha)}` : "";
|
|
1242
|
+
return `${identity.repoUrl}${ref}${commit}`;
|
|
1243
|
+
}
|
|
1244
|
+
return identity.gitRef ?? identity.version ?? identity.commitSha ?? identity.kind;
|
|
1245
|
+
}
|
|
1246
|
+
function formatArtifact(artifact) {
|
|
1247
|
+
return artifact.version ? `${artifact.version}@${artifact.ref}` : artifact.ref;
|
|
1248
|
+
}
|
|
1249
|
+
function identitiesDiffer(requested, fresh, served) {
|
|
1250
|
+
if (!served)
|
|
1251
|
+
return Boolean(requested || fresh);
|
|
1252
|
+
return Boolean(requested && requested !== served || fresh && fresh !== served);
|
|
1253
|
+
}
|
|
1254
|
+
function identitiesMateriallyDiffer(left, right) {
|
|
1255
|
+
if (!left || !right)
|
|
1256
|
+
return Boolean(left || right);
|
|
1257
|
+
return stripShortCommit(left) !== stripShortCommit(right);
|
|
1258
|
+
}
|
|
1259
|
+
function stripShortCommit(value) {
|
|
1260
|
+
return value.replace(/[@#][0-9a-f]{7}$/i, "");
|
|
1261
|
+
}
|
|
1262
|
+
function shortSha(value) {
|
|
1263
|
+
return /^[0-9a-f]{12,}$/i.test(value) ? value.slice(0, 7) : value;
|
|
1264
|
+
}
|
|
1265
|
+
|
|
1064
1266
|
// src/shared/grep-repo-response.ts
|
|
1065
1267
|
var UTF8_ENCODER = new TextEncoder;
|
|
1066
1268
|
function buildGrepRepoSuccessPayload(result, options) {
|
|
@@ -1102,6 +1304,9 @@ function buildGrepRepoSuccessPayload(result, options) {
|
|
|
1102
1304
|
if (result.resolution) {
|
|
1103
1305
|
envelope.resolution = projectResolution(result.resolution);
|
|
1104
1306
|
}
|
|
1307
|
+
const targetResolution = projectTargetResolution(result.targetResolution);
|
|
1308
|
+
if (targetResolution)
|
|
1309
|
+
envelope.targetResolution = targetResolution;
|
|
1105
1310
|
const filter = buildFilterBlock(options);
|
|
1106
1311
|
if (filter)
|
|
1107
1312
|
envelope.filter = filter;
|
|
@@ -1189,8 +1394,11 @@ function buildFilterBlock(options) {
|
|
|
1189
1394
|
return Object.keys(filter).length > 0 ? filter : undefined;
|
|
1190
1395
|
}
|
|
1191
1396
|
function formatGrepRepoTerminal(envelope, options) {
|
|
1192
|
-
if (envelope.matches.length === 0) {
|
|
1193
|
-
return {
|
|
1397
|
+
if (envelope.matches.length === 0 && !options.verbose) {
|
|
1398
|
+
return {
|
|
1399
|
+
stdout: "",
|
|
1400
|
+
stderr: formatTerminalNotes(envelope, options.useColors)
|
|
1401
|
+
};
|
|
1194
1402
|
}
|
|
1195
1403
|
const blocks = buildRenderBlocks(envelope.matches);
|
|
1196
1404
|
return options.verbose ? formatVerbose(envelope, blocks, options) : formatPlain(envelope, blocks, options);
|
|
@@ -1247,6 +1455,10 @@ function formatVerbose(envelope, blocks, options) {
|
|
|
1247
1455
|
}
|
|
1248
1456
|
lines.push("");
|
|
1249
1457
|
const blocksByFile = groupBlocksByFile(blocks);
|
|
1458
|
+
if (blocksByFile.size === 0) {
|
|
1459
|
+
lines.push("No matches.");
|
|
1460
|
+
lines.push("");
|
|
1461
|
+
}
|
|
1250
1462
|
for (const [filePath, fileBlocks] of blocksByFile) {
|
|
1251
1463
|
lines.push(colorize(filePath, "bold", options.useColors));
|
|
1252
1464
|
const gutterWidth = widestLineNumberInBlocks(fileBlocks);
|
|
@@ -1418,6 +1630,9 @@ function formatTerminalNotes(envelope, useColors) {
|
|
|
1418
1630
|
if (envelope.hasMore && envelope.nextCursor) {
|
|
1419
1631
|
lines.push(dim(`More grep results available — rerun with --cursor ${shellQuote(envelope.nextCursor)}`, useColors));
|
|
1420
1632
|
}
|
|
1633
|
+
for (const note of buildTargetResolutionNotes(envelope.targetResolution)) {
|
|
1634
|
+
lines.push(dim(note, useColors));
|
|
1635
|
+
}
|
|
1421
1636
|
if (lines.length === 0)
|
|
1422
1637
|
return;
|
|
1423
1638
|
return `${lines.join(`
|
|
@@ -1548,6 +1763,9 @@ function buildTrailer(envelope) {
|
|
|
1548
1763
|
if (skipNotes.length > 0) {
|
|
1549
1764
|
lines.push(`Note: ${skipNotes.join(", ")}.`);
|
|
1550
1765
|
}
|
|
1766
|
+
for (const note of buildTargetResolutionNotes(envelope.targetResolution)) {
|
|
1767
|
+
lines.push(note);
|
|
1768
|
+
}
|
|
1551
1769
|
return lines;
|
|
1552
1770
|
}
|
|
1553
1771
|
function buildRenderBlocks2(matches) {
|
|
@@ -1668,6 +1886,7 @@ function renderListFilesText(envelope) {
|
|
|
1668
1886
|
lines.push("");
|
|
1669
1887
|
if (envelope.files.length === 0) {
|
|
1670
1888
|
lines.push(envelope.hint ?? "No files match the requested filter.");
|
|
1889
|
+
appendTargetResolutionNotes(lines, envelope);
|
|
1671
1890
|
return lines.join(`
|
|
1672
1891
|
`);
|
|
1673
1892
|
}
|
|
@@ -1682,9 +1901,18 @@ function renderListFilesText(envelope) {
|
|
|
1682
1901
|
lines.push("");
|
|
1683
1902
|
lines.push(envelope.hint);
|
|
1684
1903
|
}
|
|
1904
|
+
appendTargetResolutionNotes(lines, envelope);
|
|
1685
1905
|
return lines.join(`
|
|
1686
1906
|
`);
|
|
1687
1907
|
}
|
|
1908
|
+
function appendTargetResolutionNotes(lines, envelope) {
|
|
1909
|
+
const notes = buildTargetResolutionNotes(envelope.targetResolution);
|
|
1910
|
+
if (notes.length === 0)
|
|
1911
|
+
return;
|
|
1912
|
+
lines.push("");
|
|
1913
|
+
for (const note of notes)
|
|
1914
|
+
lines.push(note);
|
|
1915
|
+
}
|
|
1688
1916
|
function buildHeader2(envelope) {
|
|
1689
1917
|
const identity = buildIdentity(envelope);
|
|
1690
1918
|
const countValue = envelope.hasMore ? `${envelope.files.length}+` : String(envelope.total);
|
|
@@ -2203,7 +2431,7 @@ function buildDirectVersionLookup(graph) {
|
|
|
2203
2431
|
if (!fromRoot)
|
|
2204
2432
|
continue;
|
|
2205
2433
|
const node = graph.nodes[edge.toIndex];
|
|
2206
|
-
if (!node
|
|
2434
|
+
if (!node?.version)
|
|
2207
2435
|
continue;
|
|
2208
2436
|
if (!out.has(node.name)) {
|
|
2209
2437
|
out.set(node.name, node.version);
|
|
@@ -5176,6 +5404,9 @@ function buildReadFileSuccessPayload(result, options) {
|
|
|
5176
5404
|
} else if (result.content != null) {
|
|
5177
5405
|
envelope.content = result.content;
|
|
5178
5406
|
}
|
|
5407
|
+
const targetResolution = projectTargetResolution(result.targetResolution);
|
|
5408
|
+
if (targetResolution)
|
|
5409
|
+
envelope.targetResolution = targetResolution;
|
|
5179
5410
|
return envelope;
|
|
5180
5411
|
}
|
|
5181
5412
|
function formatReadFileTerminal(envelope, options) {
|
|
@@ -5230,10 +5461,19 @@ function formatVerboseBody(envelope, options) {
|
|
|
5230
5461
|
lines.push("");
|
|
5231
5462
|
lines.push(dim(envelope.hint, options.useColors));
|
|
5232
5463
|
}
|
|
5464
|
+
appendTargetResolutionNotes2(lines, envelope, options);
|
|
5233
5465
|
lines.push("");
|
|
5234
5466
|
return lines.join(`
|
|
5235
5467
|
`);
|
|
5236
5468
|
}
|
|
5469
|
+
function appendTargetResolutionNotes2(lines, envelope, options) {
|
|
5470
|
+
const notes = buildTargetResolutionNotes(envelope.targetResolution);
|
|
5471
|
+
if (notes.length === 0)
|
|
5472
|
+
return;
|
|
5473
|
+
lines.push("");
|
|
5474
|
+
for (const note of notes)
|
|
5475
|
+
lines.push(dim(note, options.useColors));
|
|
5476
|
+
}
|
|
5237
5477
|
function splitReadFileContentLines(envelope) {
|
|
5238
5478
|
if (!envelope.content)
|
|
5239
5479
|
return [];
|
|
@@ -5295,6 +5535,12 @@ function renderReadFileText(envelope) {
|
|
|
5295
5535
|
lines.push("");
|
|
5296
5536
|
lines.push(`hint: ${envelope.hint}`);
|
|
5297
5537
|
}
|
|
5538
|
+
const resolutionNotes = buildTargetResolutionNotes(envelope.targetResolution);
|
|
5539
|
+
if (resolutionNotes.length > 0) {
|
|
5540
|
+
lines.push("");
|
|
5541
|
+
for (const note of resolutionNotes)
|
|
5542
|
+
lines.push(note);
|
|
5543
|
+
}
|
|
5298
5544
|
return lines.join(`
|
|
5299
5545
|
`);
|
|
5300
5546
|
}
|
|
@@ -5758,7 +6004,7 @@ auth.storage = "file". File storage is plaintext on disk.
|
|
|
5758
6004
|
Use --no-browser in environments without a display (CI, SSH sessions)
|
|
5759
6005
|
to get a URL you can open on another device.`;
|
|
5760
6006
|
function registerLoginCommand(program) {
|
|
5761
|
-
program.command("login").summary("
|
|
6007
|
+
program.command("login").summary("Sign in to your GitHits account").description(LOGIN_DESCRIPTION).option("--no-browser", "Print URL instead of opening browser").option("--port <port>", "Port for local callback server", parseInt).option("--force", "Re-authenticate even if already logged in").action(async (options) => {
|
|
5762
6008
|
const deps = await createAuthCommandDependencies();
|
|
5763
6009
|
await loginAction(options, deps);
|
|
5764
6010
|
});
|
|
@@ -5900,6 +6146,61 @@ function getPostLoginContinuationMessage(command) {
|
|
|
5900
6146
|
return;
|
|
5901
6147
|
}
|
|
5902
6148
|
}
|
|
6149
|
+
// src/shared/spinner.ts
|
|
6150
|
+
var SPINNER_FRAMES = ["|", "/", "-", "\\"];
|
|
6151
|
+
var FRAME_INTERVAL_MS = 80;
|
|
6152
|
+
var MESSAGE_INTERVAL_MS = 2000;
|
|
6153
|
+
function startSpinner(message, enabled = true, runtime = {}) {
|
|
6154
|
+
const stdoutIsTTY = runtime.stdoutIsTTY ?? process.stdout.isTTY;
|
|
6155
|
+
const stderrIsTTY = runtime.stderrIsTTY ?? process.stderr.isTTY;
|
|
6156
|
+
const writeStderr = runtime.writeStderr ?? ((chunk) => process.stderr.write(chunk));
|
|
6157
|
+
if (!enabled || !stdoutIsTTY || !stderrIsTTY) {
|
|
6158
|
+
return { stop: () => {} };
|
|
6159
|
+
}
|
|
6160
|
+
const messages = typeof message === "string" ? [message] : message;
|
|
6161
|
+
const framesPerMessage = Math.round(MESSAGE_INTERVAL_MS / FRAME_INTERVAL_MS);
|
|
6162
|
+
const useColors = runtime.useColors ?? process.env.NO_COLOR === undefined;
|
|
6163
|
+
let frame = 0;
|
|
6164
|
+
const render = () => {
|
|
6165
|
+
const glyph = SPINNER_FRAMES[frame % SPINNER_FRAMES.length] ?? "|";
|
|
6166
|
+
const label = messages[Math.floor(frame / framesPerMessage) % messages.length] ?? "";
|
|
6167
|
+
frame += 1;
|
|
6168
|
+
writeStderr(`\r\x1B[2K${colorizeBrand(glyph, "primary", useColors)} ${label}`);
|
|
6169
|
+
};
|
|
6170
|
+
render();
|
|
6171
|
+
const interval = setInterval(render, FRAME_INTERVAL_MS);
|
|
6172
|
+
return {
|
|
6173
|
+
stop: () => {
|
|
6174
|
+
clearInterval(interval);
|
|
6175
|
+
writeStderr("\r\x1B[2K");
|
|
6176
|
+
}
|
|
6177
|
+
};
|
|
6178
|
+
}
|
|
6179
|
+
// src/shared/spinner-messages.ts
|
|
6180
|
+
var SPINNER_MESSAGES = {
|
|
6181
|
+
example: [
|
|
6182
|
+
"Searching real implementations...",
|
|
6183
|
+
"Exploring open-source code...",
|
|
6184
|
+
"Finding production patterns...",
|
|
6185
|
+
"Grounding results..."
|
|
6186
|
+
],
|
|
6187
|
+
search: [
|
|
6188
|
+
"Exploring repositories...",
|
|
6189
|
+
"Tracing symbols...",
|
|
6190
|
+
"Inspecting dependencies...",
|
|
6191
|
+
"Scanning source code..."
|
|
6192
|
+
],
|
|
6193
|
+
code: [
|
|
6194
|
+
"Inspecting source code...",
|
|
6195
|
+
"Resolving symbols...",
|
|
6196
|
+
"Reading dependency internals..."
|
|
6197
|
+
],
|
|
6198
|
+
docs: [
|
|
6199
|
+
"Reading documentation...",
|
|
6200
|
+
"Resolving references...",
|
|
6201
|
+
"Collecting package docs..."
|
|
6202
|
+
]
|
|
6203
|
+
};
|
|
5903
6204
|
// src/shared/unified-search-request.ts
|
|
5904
6205
|
var DEFAULT_UNIFIED_SEARCH_LIMIT = 10;
|
|
5905
6206
|
function buildUnifiedSearchParams(input) {
|
|
@@ -6316,6 +6617,15 @@ function compactProgressTarget(target) {
|
|
|
6316
6617
|
payload.indexingRef = target.indexingRef;
|
|
6317
6618
|
if (target.requestedRefKind)
|
|
6318
6619
|
payload.requestedRefKind = target.requestedRefKind;
|
|
6620
|
+
const targetResolution = projectTargetResolution(target.targetResolution);
|
|
6621
|
+
if (targetResolution)
|
|
6622
|
+
payload.targetResolution = targetResolution;
|
|
6623
|
+
if (target.availableVersions?.length) {
|
|
6624
|
+
payload.availableVersions = target.availableVersions;
|
|
6625
|
+
}
|
|
6626
|
+
if (target.availableRefs?.length) {
|
|
6627
|
+
payload.availableRefs = target.availableRefs;
|
|
6628
|
+
}
|
|
6319
6629
|
return Object.keys(payload).length > 0 ? payload : undefined;
|
|
6320
6630
|
}
|
|
6321
6631
|
function buildFilterEcho3(filters) {
|
|
@@ -6358,7 +6668,11 @@ function buildProgressFreshnessWarnings(progress) {
|
|
|
6358
6668
|
requestedTarget: target.requested,
|
|
6359
6669
|
freshTarget: target.resolvedRequested,
|
|
6360
6670
|
servedTarget: target.served
|
|
6361
|
-
})).filter((entry) => Boolean(entry));
|
|
6671
|
+
})).concat((progress?.targets ?? []).map((target) => progressTargetResolutionWarning(target)).filter((entry) => Boolean(entry))).filter((entry) => Boolean(entry));
|
|
6672
|
+
}
|
|
6673
|
+
function progressTargetResolutionWarning(target) {
|
|
6674
|
+
const notes = buildTargetResolutionNotes(target.targetResolution ?? buildResolutionFromRetryCandidates(target));
|
|
6675
|
+
return notes.length > 0 ? notes.join(" ") : undefined;
|
|
6362
6676
|
}
|
|
6363
6677
|
function freshnessWarning(input) {
|
|
6364
6678
|
if (!isTrustRelevantFreshness(input.freshness))
|
|
@@ -6377,7 +6691,30 @@ function labelsDiverge(input) {
|
|
|
6377
6691
|
const served = input.servedTarget;
|
|
6378
6692
|
if (!served)
|
|
6379
6693
|
return false;
|
|
6380
|
-
return Boolean(input.freshTarget && input.freshTarget !== served
|
|
6694
|
+
return Boolean(input.freshTarget && canonicalTargetLabel(input.freshTarget) !== canonicalTargetLabel(served));
|
|
6695
|
+
}
|
|
6696
|
+
function canonicalTargetLabel(label) {
|
|
6697
|
+
const parsed = parsePackageVersionLabel(label);
|
|
6698
|
+
if (!parsed)
|
|
6699
|
+
return label;
|
|
6700
|
+
const version2 = parsed.version.replace(/^v(?=\d)/i, "");
|
|
6701
|
+
return `${parsed.registry.toLowerCase()}:${parsed.packageName}@${version2}`;
|
|
6702
|
+
}
|
|
6703
|
+
function parsePackageVersionLabel(label) {
|
|
6704
|
+
const registryEnd = label.indexOf(":");
|
|
6705
|
+
if (registryEnd <= 0)
|
|
6706
|
+
return;
|
|
6707
|
+
const versionStart = label.lastIndexOf("@");
|
|
6708
|
+
if (versionStart <= registryEnd + 1)
|
|
6709
|
+
return;
|
|
6710
|
+
const version2 = label.slice(versionStart + 1);
|
|
6711
|
+
if (!version2)
|
|
6712
|
+
return;
|
|
6713
|
+
return {
|
|
6714
|
+
registry: label.slice(0, registryEnd),
|
|
6715
|
+
packageName: label.slice(registryEnd + 1, versionStart),
|
|
6716
|
+
version: version2
|
|
6717
|
+
};
|
|
6381
6718
|
}
|
|
6382
6719
|
function warningForEntry(entry) {
|
|
6383
6720
|
const reasons = [];
|
|
@@ -6389,6 +6726,14 @@ function warningForEntry(entry) {
|
|
|
6389
6726
|
});
|
|
6390
6727
|
if (freshness)
|
|
6391
6728
|
return freshness;
|
|
6729
|
+
const terminalLifecycleReason = terminalLifecycleWarningReason(entry);
|
|
6730
|
+
if (terminalLifecycleReason) {
|
|
6731
|
+
reasons.push(terminalLifecycleReason);
|
|
6732
|
+
} else {
|
|
6733
|
+
const targetResolutionWarning = targetResolutionWarningForEntry(entry);
|
|
6734
|
+
if (targetResolutionWarning)
|
|
6735
|
+
reasons.push(targetResolutionWarning);
|
|
6736
|
+
}
|
|
6392
6737
|
if (entry.incompatibleQueryFeatures?.length) {
|
|
6393
6738
|
reasons.push(`incompatible query features [${entry.incompatibleQueryFeatures.join(", ")}]`);
|
|
6394
6739
|
}
|
|
@@ -6401,10 +6746,10 @@ function warningForEntry(entry) {
|
|
|
6401
6746
|
if (entry.ignoredFilters?.length) {
|
|
6402
6747
|
reasons.push(`ignored filters [${entry.ignoredFilters.join(", ")}]`);
|
|
6403
6748
|
}
|
|
6404
|
-
if (entry.indexingStatus) {
|
|
6749
|
+
if (!terminalLifecycleReason && reasons.length === 0 && entry.indexingStatus) {
|
|
6405
6750
|
reasons.push(`indexing status ${entry.indexingStatus}`);
|
|
6406
6751
|
}
|
|
6407
|
-
if (entry.codeIndexState) {
|
|
6752
|
+
if (!terminalLifecycleReason && reasons.length === 0 && entry.codeIndexState) {
|
|
6408
6753
|
if (entry.codeIndexState !== "STALE") {
|
|
6409
6754
|
reasons.push(`code index state ${entry.codeIndexState}`);
|
|
6410
6755
|
}
|
|
@@ -6418,6 +6763,18 @@ function warningForEntry(entry) {
|
|
|
6418
6763
|
}
|
|
6419
6764
|
return;
|
|
6420
6765
|
}
|
|
6766
|
+
function terminalLifecycleWarningReason(entry) {
|
|
6767
|
+
const states = Array.from(new Set([entry.indexingStatus, entry.codeIndexState].filter(Boolean)));
|
|
6768
|
+
const terminalStates = states.filter((state) => state !== "INDEXING" && state !== "STALE");
|
|
6769
|
+
if (terminalStates.length === 0)
|
|
6770
|
+
return;
|
|
6771
|
+
const status = terminalStates.join("/");
|
|
6772
|
+
return entry.note ? `${entry.note} (${status})` : `status ${status}`;
|
|
6773
|
+
}
|
|
6774
|
+
function targetResolutionWarningForEntry(entry) {
|
|
6775
|
+
const notes = buildTargetResolutionNotes(entry.targetResolution);
|
|
6776
|
+
return notes.length > 0 ? notes.join(" ") : undefined;
|
|
6777
|
+
}
|
|
6421
6778
|
function compactSourceStatus(sourceStatus) {
|
|
6422
6779
|
if (!sourceStatus || sourceStatus.length === 0)
|
|
6423
6780
|
return;
|
|
@@ -6447,6 +6804,13 @@ function compactSourceStatusEntry(entry) {
|
|
|
6447
6804
|
payload.codeIndexState = entry.codeIndexState;
|
|
6448
6805
|
interesting = true;
|
|
6449
6806
|
}
|
|
6807
|
+
const targetResolution = projectTargetResolution(entry.targetResolution);
|
|
6808
|
+
if (targetResolution) {
|
|
6809
|
+
payload.targetResolution = targetResolution;
|
|
6810
|
+
if (buildTargetResolutionNotes(targetResolution).length > 0) {
|
|
6811
|
+
interesting = true;
|
|
6812
|
+
}
|
|
6813
|
+
}
|
|
6450
6814
|
if (entry.indexingStatus && entry.indexingStatus !== "INDEXED") {
|
|
6451
6815
|
payload.indexingStatus = entry.indexingStatus;
|
|
6452
6816
|
interesting = true;
|
|
@@ -6484,7 +6848,7 @@ function assertSearchFollowUpInvariant(hit) {
|
|
|
6484
6848
|
if ((hit.resultType === "DOCUMENTATION_PAGE" || hit.resultType === "REPOSITORY_DOC") && !hit.locator.pageId) {
|
|
6485
6849
|
throw new MalformedCodeNavigationResponseError(`${hit.resultType} search hit missing required pageId.`);
|
|
6486
6850
|
}
|
|
6487
|
-
if (hit.resultType === "REPOSITORY_DOC" && (!hit.locator.repoUrl || !hit.locator.
|
|
6851
|
+
if (hit.resultType === "REPOSITORY_DOC" && (!hit.locator.repoUrl || !hit.locator.filePath)) {
|
|
6488
6852
|
throw new MalformedCodeNavigationResponseError("REPOSITORY_DOC search hit missing repo locator fields.");
|
|
6489
6853
|
}
|
|
6490
6854
|
}
|
|
@@ -6496,7 +6860,7 @@ function renderUnifiedSearchSuccess(payload) {
|
|
|
6496
6860
|
lines.push(buildHeader8(payload));
|
|
6497
6861
|
lines.push("");
|
|
6498
6862
|
if (payload.results.length === 0) {
|
|
6499
|
-
lines.push(payload.completed ? "No hits." :
|
|
6863
|
+
lines.push(payload.completed ? "No hits." : noHitsYetMessage(payload));
|
|
6500
6864
|
} else {
|
|
6501
6865
|
appendUnifiedSearchHits(lines, payload.results);
|
|
6502
6866
|
}
|
|
@@ -6509,6 +6873,18 @@ function renderUnifiedSearchSuccess(payload) {
|
|
|
6509
6873
|
return lines.join(`
|
|
6510
6874
|
`);
|
|
6511
6875
|
}
|
|
6876
|
+
function noHitsYetMessage(payload) {
|
|
6877
|
+
if (payload.completed)
|
|
6878
|
+
return "No hits.";
|
|
6879
|
+
const status = payload.progress?.status;
|
|
6880
|
+
if (status === "TIMEOUT")
|
|
6881
|
+
return "No hits yet - timed out waiting.";
|
|
6882
|
+
if (status === "FAILED")
|
|
6883
|
+
return "No hits - search failed.";
|
|
6884
|
+
if (status === "SEARCHING")
|
|
6885
|
+
return "No hits yet - searching.";
|
|
6886
|
+
return "No hits yet - indexing.";
|
|
6887
|
+
}
|
|
6512
6888
|
function renderUnifiedSearchError(payload) {
|
|
6513
6889
|
const lines = [];
|
|
6514
6890
|
const header = `search${SEP6}ERROR${SEP6}code=${payload.code}${payload.retryable ? `${SEP6}retryable` : ""}`;
|
|
@@ -6638,7 +7014,9 @@ function buildTrailer2(payload) {
|
|
|
6638
7014
|
lines.push(`More hits available.${nextOffsetHint}`);
|
|
6639
7015
|
}
|
|
6640
7016
|
if (!payload.completed && payload.searchRef) {
|
|
6641
|
-
|
|
7017
|
+
const status = payload.progress?.status;
|
|
7018
|
+
const action = status === "TIMEOUT" ? "Search timed out waiting for fresh data." : status === "FAILED" ? "Search failed before completion." : status === "SEARCHING" ? "Search in progress." : "Indexing in progress.";
|
|
7019
|
+
lines.push(`${action} Call search_status with searchRef=${payload.searchRef} to follow up.`);
|
|
6642
7020
|
}
|
|
6643
7021
|
if (payload.sourceStatus && payload.sourceStatus.length > 0) {
|
|
6644
7022
|
lines.push("source notes:");
|
|
@@ -6669,6 +7047,9 @@ function formatProgressTarget(target) {
|
|
|
6669
7047
|
parts.push(`intent=${target.requestedRefKind}`);
|
|
6670
7048
|
if (target.indexingRef)
|
|
6671
7049
|
parts.push(`indexingRef=${target.indexingRef}`);
|
|
7050
|
+
for (const note of buildTargetResolutionNotes(target.targetResolution ?? buildResolutionFromRetryCandidates(target))) {
|
|
7051
|
+
parts.push(note);
|
|
7052
|
+
}
|
|
6672
7053
|
return parts.length > 0 ? parts.join(SEP6) : "target progress unavailable";
|
|
6673
7054
|
}
|
|
6674
7055
|
function describeFreshness(value) {
|
|
@@ -6686,6 +7067,10 @@ function describeFreshness(value) {
|
|
|
6686
7067
|
}
|
|
6687
7068
|
}
|
|
6688
7069
|
function formatSourceStatus(entry) {
|
|
7070
|
+
const terminalReason = terminalLifecycleReason(entry);
|
|
7071
|
+
if (terminalReason) {
|
|
7072
|
+
return `${entry.source} (${entry.targetLabel})${SEP6}${terminalReason}`;
|
|
7073
|
+
}
|
|
6689
7074
|
const parts = [`${entry.source} (${entry.targetLabel})`];
|
|
6690
7075
|
if (entry.indexingStatus)
|
|
6691
7076
|
parts.push(`indexing=${entry.indexingStatus}`);
|
|
@@ -6705,8 +7090,19 @@ function formatSourceStatus(entry) {
|
|
|
6705
7090
|
}
|
|
6706
7091
|
if (entry.note)
|
|
6707
7092
|
parts.push(entry.note);
|
|
7093
|
+
for (const note of buildTargetResolutionNotes(entry.targetResolution)) {
|
|
7094
|
+
parts.push(note);
|
|
7095
|
+
}
|
|
6708
7096
|
return parts.join(SEP6);
|
|
6709
7097
|
}
|
|
7098
|
+
function terminalLifecycleReason(entry) {
|
|
7099
|
+
const states = Array.from(new Set([entry.indexingStatus, entry.codeIndexState].filter(Boolean)));
|
|
7100
|
+
const terminalStates = states.filter((state) => state !== "INDEXING" && state !== "STALE");
|
|
7101
|
+
if (terminalStates.length === 0)
|
|
7102
|
+
return;
|
|
7103
|
+
const status = terminalStates.join("/");
|
|
7104
|
+
return entry.note ? `${entry.note} (${status})` : `status ${status}`;
|
|
7105
|
+
}
|
|
6710
7106
|
function quote4(value) {
|
|
6711
7107
|
return value.includes('"') ? `'${value}'` : `"${value}"`;
|
|
6712
7108
|
}
|
|
@@ -6769,7 +7165,7 @@ function renderUnifiedSearchStatusText(payload) {
|
|
|
6769
7165
|
`);
|
|
6770
7166
|
}
|
|
6771
7167
|
function buildHeader9(payload) {
|
|
6772
|
-
const state = payload.completed ? "complete" : "
|
|
7168
|
+
const state = payload.completed ? "complete" : payload.progress?.status.toLowerCase() ?? "incomplete";
|
|
6773
7169
|
const parts = [`search_status${SEP7}${state}`];
|
|
6774
7170
|
if (payload.searchRef)
|
|
6775
7171
|
parts.push(`searchRef=${payload.searchRef}`);
|
|
@@ -6797,32 +7193,10 @@ function appendResult(lines, result) {
|
|
|
6797
7193
|
lines.push("");
|
|
6798
7194
|
lines.push("source notes:");
|
|
6799
7195
|
for (const entry of result.sourceStatus) {
|
|
6800
|
-
lines.push(` - ${
|
|
7196
|
+
lines.push(` - ${formatSourceStatus(entry)}`);
|
|
6801
7197
|
}
|
|
6802
7198
|
}
|
|
6803
7199
|
}
|
|
6804
|
-
function formatSourceStatus2(entry) {
|
|
6805
|
-
const parts = [`${entry.source} (${entry.targetLabel})`];
|
|
6806
|
-
if (entry.indexingStatus)
|
|
6807
|
-
parts.push(`indexing=${entry.indexingStatus}`);
|
|
6808
|
-
if (entry.codeIndexState)
|
|
6809
|
-
parts.push(`codeIndex=${entry.codeIndexState}`);
|
|
6810
|
-
if (entry.ignoredFilters?.length) {
|
|
6811
|
-
parts.push(`ignored=${entry.ignoredFilters.join(",")}`);
|
|
6812
|
-
}
|
|
6813
|
-
if (entry.incompatibleFilters?.length) {
|
|
6814
|
-
parts.push(`incompatible=${entry.incompatibleFilters.join(",")}`);
|
|
6815
|
-
}
|
|
6816
|
-
if (entry.ignoredQueryFeatures?.length) {
|
|
6817
|
-
parts.push(`ignoredQuery=${entry.ignoredQueryFeatures.join(",")}`);
|
|
6818
|
-
}
|
|
6819
|
-
if (entry.incompatibleQueryFeatures?.length) {
|
|
6820
|
-
parts.push(`incompatibleQuery=${entry.incompatibleQueryFeatures.join(",")}`);
|
|
6821
|
-
}
|
|
6822
|
-
if (entry.note)
|
|
6823
|
-
parts.push(entry.note);
|
|
6824
|
-
return parts.join(SEP7);
|
|
6825
|
-
}
|
|
6826
7200
|
function formatProgress(progress) {
|
|
6827
7201
|
const next = progress.next ? `; next: ${progress.next}` : "";
|
|
6828
7202
|
return `progress: ${progress.status}, ${progress.targetsReady}/${progress.targetsTotal} targets ready, ${progress.elapsedMs}ms elapsed${next}`;
|
|
@@ -7042,6 +7416,9 @@ function buildListFilesSuccessPayload(result, options) {
|
|
|
7042
7416
|
envelope.indexedVersion = result.indexedVersion;
|
|
7043
7417
|
if (result.resolution)
|
|
7044
7418
|
envelope.resolution = projectResolution2(result.resolution);
|
|
7419
|
+
const targetResolution = projectTargetResolution(result.targetResolution);
|
|
7420
|
+
if (targetResolution)
|
|
7421
|
+
envelope.targetResolution = targetResolution;
|
|
7045
7422
|
if (result.hint)
|
|
7046
7423
|
envelope.hint = result.hint;
|
|
7047
7424
|
const filter = buildFilterBlock2(options);
|
|
@@ -7152,6 +7529,7 @@ function formatVerbose2(envelope, options) {
|
|
|
7152
7529
|
if (envelope.resolution || envelope.indexedVersion) {
|
|
7153
7530
|
lines.push(buildResolutionLine(envelope, options));
|
|
7154
7531
|
}
|
|
7532
|
+
appendTargetResolutionNotes3(lines, envelope, options);
|
|
7155
7533
|
lines.push("");
|
|
7156
7534
|
const pathWidth = longestPathLength(envelope.files);
|
|
7157
7535
|
for (const file of envelope.files) {
|
|
@@ -7180,6 +7558,7 @@ function formatEmpty(envelope, options, verbose) {
|
|
|
7180
7558
|
if (envelope.resolution || envelope.indexedVersion) {
|
|
7181
7559
|
lines.push(buildResolutionLine(envelope, options));
|
|
7182
7560
|
}
|
|
7561
|
+
appendTargetResolutionNotes3(lines, envelope, options);
|
|
7183
7562
|
lines.push("");
|
|
7184
7563
|
lines.push(dim(hint, options.useColors));
|
|
7185
7564
|
lines.push("");
|
|
@@ -7202,6 +7581,11 @@ function buildResolutionLine(envelope, options) {
|
|
|
7202
7581
|
parts.push(`commit ${commit.slice(0, 7)}`);
|
|
7203
7582
|
return dim(parts.join(" · "), options.useColors);
|
|
7204
7583
|
}
|
|
7584
|
+
function appendTargetResolutionNotes3(lines, envelope, options) {
|
|
7585
|
+
const notes = buildTargetResolutionNotes(envelope.targetResolution);
|
|
7586
|
+
for (const note of notes)
|
|
7587
|
+
lines.push(dim(note, options.useColors));
|
|
7588
|
+
}
|
|
7205
7589
|
function buildIdentityLabel(envelope) {
|
|
7206
7590
|
if (envelope.registry && envelope.name) {
|
|
7207
7591
|
return `${envelope.name} · ${envelope.registry}`;
|
|
@@ -7251,13 +7635,10 @@ function resolveCliCodeNavTarget(spec, options) {
|
|
|
7251
7635
|
const hasRepoUrl = Boolean(options.repoUrl);
|
|
7252
7636
|
const hasGitRef = Boolean(options.gitRef);
|
|
7253
7637
|
if (hasSpec && (hasRepoUrl || hasGitRef)) {
|
|
7254
|
-
throw new InvalidPackageSpecError("Provide either a package spec (e.g. `npm:express`) or `--repo-url`
|
|
7638
|
+
throw new InvalidPackageSpecError("Provide either a package spec (e.g. `npm:express`) or `--repo-url` with optional `--git-ref`, not both.");
|
|
7255
7639
|
}
|
|
7256
7640
|
if (!hasSpec && !hasRepoUrl) {
|
|
7257
|
-
throw new InvalidPackageSpecError("A package spec (e.g. `npm:express`) or `--repo-url`
|
|
7258
|
-
}
|
|
7259
|
-
if (hasRepoUrl && !hasGitRef) {
|
|
7260
|
-
throw new InvalidPackageSpecError("`--repo-url` requires `--git-ref` for code files/read/grep (a tag, branch, commit, or `HEAD`).");
|
|
7641
|
+
throw new InvalidPackageSpecError("A package spec (e.g. `npm:express`) or `--repo-url` is required.");
|
|
7261
7642
|
}
|
|
7262
7643
|
if (hasSpec) {
|
|
7263
7644
|
const parsed = parsePackageSpec(spec);
|
|
@@ -7301,6 +7682,13 @@ function formatIndexingError(mapped) {
|
|
|
7301
7682
|
const suffix = more > 0 ? ` (+${more} more)` : "";
|
|
7302
7683
|
lines.push(` already-indexed versions: ${shown}${suffix}`);
|
|
7303
7684
|
}
|
|
7685
|
+
const refs = detail.availableRefs;
|
|
7686
|
+
if (refs && refs.length > 0) {
|
|
7687
|
+
const shown = refs.slice(0, 5).map((entry) => entry.ref).join(", ");
|
|
7688
|
+
const more = refs.length - 5;
|
|
7689
|
+
const suffix = more > 0 ? ` (+${more} more)` : "";
|
|
7690
|
+
lines.push(` already-indexed refs: ${shown}${suffix}`);
|
|
7691
|
+
}
|
|
7304
7692
|
return lines.join(`
|
|
7305
7693
|
`);
|
|
7306
7694
|
}
|
|
@@ -7381,7 +7769,8 @@ async function pkgFilesAction(firstArg, secondArg, options, deps) {
|
|
|
7381
7769
|
limit,
|
|
7382
7770
|
waitTimeoutMs: wait
|
|
7383
7771
|
});
|
|
7384
|
-
const
|
|
7772
|
+
const spinner = startSpinner(SPINNER_MESSAGES.code, !options.json);
|
|
7773
|
+
const result = await deps.codeNavigationService.listFiles(build.params).finally(() => spinner.stop());
|
|
7385
7774
|
const payload = buildListFilesSuccessPayload(result, {
|
|
7386
7775
|
registry: target.registry ? toPkgseerRegistryLowercase(target.registry) : undefined,
|
|
7387
7776
|
name: target.packageName,
|
|
@@ -7439,7 +7828,7 @@ function resolvePositionals(firstArg, secondArg, hasRepoUrl) {
|
|
|
7439
7828
|
throw new InvalidPackageSpecError("In --repo-url mode, pass only [path-prefix] — the package spec is replaced by --repo-url.");
|
|
7440
7829
|
}
|
|
7441
7830
|
if (firstArg && REGISTRY_SPEC_HINT.test(firstArg)) {
|
|
7442
|
-
throw new InvalidPackageSpecError(`'${firstArg}' looks like a package spec. Provide either a package spec or \`--repo-url\`
|
|
7831
|
+
throw new InvalidPackageSpecError(`'${firstArg}' looks like a package spec. Provide either a package spec or \`--repo-url\` with optional \`--git-ref\`, not both.`);
|
|
7443
7832
|
}
|
|
7444
7833
|
return { spec: undefined, pathPrefix: firstArg };
|
|
7445
7834
|
}
|
|
@@ -7467,7 +7856,7 @@ On an INDEXING response, the dependency is being indexed on-demand
|
|
|
7467
7856
|
— retry with a longer --wait (up to 60000 ms) or pick one of the
|
|
7468
7857
|
already-indexed versions surfaced in the error detail.`;
|
|
7469
7858
|
function registerCodeFilesCommand(pkgCommand) {
|
|
7470
|
-
return pkgCommand.command("files").summary("List files in an indexed dependency").description(PKG_FILES_DESCRIPTION).argument("[spec-or-prefix]", "Spec mode: package spec (e.g. npm:express). Repo mode (with --repo-url): the path-prefix.").argument("[path-prefix]", "Spec mode only: literal directory prefix (not a glob). Ignored with --repo-url.").option("--repo-url <url>", "Repository URL addressing (
|
|
7859
|
+
return pkgCommand.command("files").summary("List files in an indexed dependency").description(PKG_FILES_DESCRIPTION).argument("[spec-or-prefix]", "Spec mode: package spec (e.g. npm:express). Repo mode (with --repo-url): the path-prefix.").argument("[path-prefix]", "Spec mode only: literal directory prefix (not a glob). Ignored with --repo-url.").option("--repo-url <url>", "Repository URL addressing (defaults to the repo default branch)").option("--git-ref <ref>", "Optional tag, commit, branch, or HEAD for --repo-url.").option("--path <path>", "Exact file selector").option("--glob <glob>", "Glob selector (repeatable)", collectRepeatable).option("--ext <ext>", "Extension filter without leading dot (repeatable)", collectRepeatable).option("--file-type <type>", "File type filter such as source or doc (repeatable)", collectRepeatable).option("--language <language>", "Language filter matching aigrep language names (repeatable)", collectRepeatable).option("--file-intent <intent>", "Inclusive file-intent filter. Repeat to include multiple intents: production, test, benchmark, example, generated, fixture, build, vendor", collectRepeatable).option("--exclude-intent <intent>", "Exclude these file intents after inclusive filtering (repeatable)", collectRepeatable).option("--exclude-docs", "Skip files classified as documentation").option("--exclude-tests", "Skip files classified as tests").option("--hidden", "Include dotfiles and dot-prefixed paths").option("--limit <n>", "Max entries (1-1000, default 200)").option("--wait <ms>", `Indexing wait timeout (0-${MAX_WAIT_TIMEOUT_MS}, default ${DEFAULT_WAIT_TIMEOUT_MS})`).option("-v, --verbose", "Annotate each path with language / file-type / byte size").option("--json", "Emit the JSON envelope").action(async (arg1, arg2, options) => {
|
|
7471
7860
|
const deps = await createContainer();
|
|
7472
7861
|
await pkgFilesAction(arg1, arg2, options, {
|
|
7473
7862
|
codeNavigationService: deps.codeNavigationService,
|
|
@@ -7517,7 +7906,8 @@ async function pkgGrepAction(first, second, third, options, deps) {
|
|
|
7517
7906
|
symbolFields: options.symbolField,
|
|
7518
7907
|
waitTimeoutMs: wait
|
|
7519
7908
|
});
|
|
7520
|
-
const
|
|
7909
|
+
const spinner = startSpinner(SPINNER_MESSAGES.code, !options.json);
|
|
7910
|
+
const result = await deps.codeNavigationService.grepRepo(build.params).finally(() => spinner.stop());
|
|
7521
7911
|
const payload = buildGrepRepoSuccessPayload(result, {
|
|
7522
7912
|
registry: target.registry ? toPkgseerRegistryLowercase(target.registry) : undefined,
|
|
7523
7913
|
name: target.packageName,
|
|
@@ -7544,7 +7934,7 @@ async function pkgGrepAction(first, second, third, options, deps) {
|
|
|
7544
7934
|
if (options.json) {
|
|
7545
7935
|
console.log(JSON.stringify(payload));
|
|
7546
7936
|
if (payload.totalMatches === 0)
|
|
7547
|
-
process.
|
|
7937
|
+
process.exitCode = 1;
|
|
7548
7938
|
return;
|
|
7549
7939
|
}
|
|
7550
7940
|
const rendered = formatGrepRepoTerminal(payload, {
|
|
@@ -7557,7 +7947,7 @@ async function pkgGrepAction(first, second, third, options, deps) {
|
|
|
7557
7947
|
if (rendered.stderr)
|
|
7558
7948
|
process.stderr.write(rendered.stderr);
|
|
7559
7949
|
if (payload.totalMatches === 0)
|
|
7560
|
-
process.
|
|
7950
|
+
process.exitCode = 1;
|
|
7561
7951
|
} catch (error2) {
|
|
7562
7952
|
handleCodeNavCommandError(error2, options.json ?? false, formatFileErrorWithFilesHint, 2);
|
|
7563
7953
|
}
|
|
@@ -7570,7 +7960,7 @@ function resolvePositionals2(first, second, third, hasRepoUrl) {
|
|
|
7570
7960
|
return { spec: undefined, pattern: first, pathPrefix: second };
|
|
7571
7961
|
}
|
|
7572
7962
|
if (first !== undefined && second === undefined) {
|
|
7573
|
-
throw new InvalidPackageSpecError("In spec mode, pass at least <spec> <pattern>. If you meant to target a repository instead, pass --repo-url <url> --git-ref <ref>.");
|
|
7963
|
+
throw new InvalidPackageSpecError("In spec mode, pass at least <spec> <pattern>. If you meant to target a repository instead, pass --repo-url <url> with optional --git-ref <ref>.");
|
|
7574
7964
|
}
|
|
7575
7965
|
return { spec: first, pattern: second, pathPrefix: third };
|
|
7576
7966
|
}
|
|
@@ -7595,7 +7985,7 @@ var PKG_GREP_DESCRIPTION = `Deterministic text grep over indexed dependency and
|
|
|
7595
7985
|
${CLI_GREP_PATTERN_NOTE}
|
|
7596
7986
|
Use \`githits search\` for discovery; use \`githits code grep\` when you know the text or regex to match.
|
|
7597
7987
|
|
|
7598
|
-
Addressing: <spec> (registry:name[@version]) OR --repo-url <url> --git-ref <ref
|
|
7988
|
+
Addressing: <spec> (registry:name[@version]) OR --repo-url <url> [--git-ref <ref>].
|
|
7599
7989
|
Omitted version means latest release.
|
|
7600
7990
|
In spec mode pass <spec> <pattern> [path-prefix]; in repo-URL mode pass only <pattern> [path-prefix].
|
|
7601
7991
|
|
|
@@ -7610,8 +8000,8 @@ for context, --verbose for grouped output, and --cursor to continue a paginated
|
|
|
7610
8000
|
grep run. --symbol-field hydrates enclosing symbol metadata (appears under each
|
|
7611
8001
|
match in --verbose output; full payload in --json).`;
|
|
7612
8002
|
function registerCodeGrepCommand(pkgCommand) {
|
|
7613
|
-
return pkgCommand.command("grep").summary("Deterministic text grep over indexed dependency source").description(PKG_GREP_DESCRIPTION).argument("[spec-or-pattern]", "Spec mode: package spec (e.g. npm:express). Repo mode (with --repo-url): the pattern.").argument("[pattern-or-prefix]", "Spec mode: the pattern. Repo mode: optional path-prefix.").argument("[path-prefix]", "Spec mode only: optional path-prefix. Ignored with --repo-url.").option("--repo-url <url>", "Repository URL addressing (
|
|
7614
|
-
const { createContainer: createContainer2 } = await import("./shared/chunk-
|
|
8003
|
+
return pkgCommand.command("grep").summary("Deterministic text grep over indexed dependency source").description(PKG_GREP_DESCRIPTION).argument("[spec-or-pattern]", "Spec mode: package spec (e.g. npm:express). Repo mode (with --repo-url): the pattern.").argument("[pattern-or-prefix]", "Spec mode: the pattern. Repo mode: optional path-prefix.").argument("[path-prefix]", "Spec mode only: optional path-prefix. Ignored with --repo-url.").option("--repo-url <url>", "Repository URL addressing (defaults to the repo default branch)").option("--git-ref <ref>", "Optional tag, commit, branch, or HEAD for --repo-url.").option("--path <path>", "Exact file path to grep").option("--glob <glob>", "Glob scope (repeatable)", collectRepeatable2, []).option("--ext <ext>", "Extension filter without leading dot (repeatable)", collectRepeatable2, []).option("--regex", "Interpret the pattern as RE2 regex").option("--case-sensitive", "Enable ASCII case-sensitive matching").option("-C, --context <n>", "Context lines before and after each match (0-10)").option("-B, --before-context <n>", "Context lines before each match (0-10)").option("-A, --after-context <n>", "Context lines after each match (0-10)").option("--exclude-docs", "Skip files classified as documentation").option("--exclude-tests", "Skip files classified as tests").option("--limit <n>", "Max matches to return on this page (1-1000, default 50)").option("--per-file-limit <n>", "Cap matches per file within this page (0-1000, 0 = unlimited)").option("--cursor <cursor>", "Opaque nextCursor from a previous grep result").option("--symbol-field <field>", `Repeatable; surfaces in --json and under each --verbose match. ${GREP_REPO_SYMBOL_FIELDS_NOTE}`, collectRepeatable2, []).option("--wait <ms>", `Indexing wait timeout (0-${MAX_WAIT_TIMEOUT_MS}, default ${DEFAULT_WAIT_TIMEOUT_MS})`).option("-v, --verbose", "Render grouped output with file headers").option("--json", "Emit the JSON envelope").action(async (arg1, arg2, arg3, options) => {
|
|
8004
|
+
const { createContainer: createContainer2 } = await import("./shared/chunk-70bn2pmx.js");
|
|
7615
8005
|
const deps = await createContainer2();
|
|
7616
8006
|
await pkgGrepAction(arg1, arg2, arg3, options, {
|
|
7617
8007
|
codeNavigationService: deps.codeNavigationService,
|
|
@@ -7721,7 +8111,8 @@ async function pkgReadAction(firstArg, secondArg, options, deps) {
|
|
|
7721
8111
|
endLine: range.endLine,
|
|
7722
8112
|
waitTimeoutMs: wait
|
|
7723
8113
|
});
|
|
7724
|
-
const
|
|
8114
|
+
const spinner = startSpinner(SPINNER_MESSAGES.code, !options.json);
|
|
8115
|
+
const result = await deps.codeNavigationService.readFile(build.params).finally(() => spinner.stop());
|
|
7725
8116
|
const payload = buildReadFileSuccessPayload(result, {
|
|
7726
8117
|
registry: target.registry ? toPkgseerRegistryLowercase(target.registry) : undefined,
|
|
7727
8118
|
name: target.packageName,
|
|
@@ -7845,7 +8236,7 @@ Binary files show a one-line sentinel instead of content. When a
|
|
|
7845
8236
|
path is missing, the response is a FILE_NOT_FOUND error — use
|
|
7846
8237
|
\`code files\` to discover available paths.`;
|
|
7847
8238
|
function registerCodeReadCommand(pkgCommand) {
|
|
7848
|
-
return pkgCommand.command("read").summary("Read a file from an indexed dependency").description(PKG_READ_DESCRIPTION).argument("[spec-or-path]", "In spec mode: package spec (e.g. npm:express). In --repo-url mode: the file path. See examples in `--help`.").argument("[path]", "File path (spec mode only — in --repo-url mode use the first positional).").option("--repo-url <url>", "Repository URL addressing (
|
|
8239
|
+
return pkgCommand.command("read").summary("Read a file from an indexed dependency").description(PKG_READ_DESCRIPTION).argument("[spec-or-path]", "In spec mode: package spec (e.g. npm:express). In --repo-url mode: the file path. See examples in `--help`.").argument("[path]", "File path (spec mode only — in --repo-url mode use the first positional).").option("--repo-url <url>", "Repository URL addressing (defaults to the repo default branch)").option("--git-ref <ref>", "Optional tag, commit, branch, or HEAD for --repo-url.").option("--lines <start-end>", "Line range (e.g. `10-40`, `10-` for open end, `-40` for open start)").option("--start <n>", "Starting line (1-indexed). Alternative to --lines.").option("--end <n>", "Ending line (inclusive). Alternative to --lines.").option("--wait <ms>", `Indexing wait timeout (0-${MAX_WAIT_TIMEOUT_MS}, default ${DEFAULT_WAIT_TIMEOUT_MS})`).option("-v, --verbose", "Render a header and a line-number gutter alongside the content").option("--json", "Emit the JSON envelope").action(async (spec, path, options) => {
|
|
7849
8240
|
const deps = await createContainer();
|
|
7850
8241
|
await pkgReadAction(spec, path, options, {
|
|
7851
8242
|
codeNavigationService: deps.codeNavigationService,
|
|
@@ -7862,7 +8253,7 @@ async function registerCodeCommandGroup(program, options = {}) {
|
|
|
7862
8253
|
if (!registration.shouldRegister) {
|
|
7863
8254
|
return;
|
|
7864
8255
|
}
|
|
7865
|
-
const codeCommand = program.command("code").summary("
|
|
8256
|
+
const codeCommand = program.command("code").summary("Inspect dependency source code and symbols").description("List files, read files, and grep substrings inside indexed dependency source. Every command accepts either `<spec>` (registry:name[@version]) or `--repo-url <url> [--git-ref <ref>]`. Omitted package versions use the latest release; omitted repo refs use the default-branch intent. For package-level metadata use `githits pkg`.");
|
|
7866
8257
|
registerCodeFilesCommand(codeCommand);
|
|
7867
8258
|
registerCodeReadCommand(codeCommand);
|
|
7868
8259
|
registerCodeGrepCommand(codeCommand);
|
|
@@ -7883,7 +8274,8 @@ async function docsListAction(spec, options, deps) {
|
|
|
7883
8274
|
limit,
|
|
7884
8275
|
after: options.after
|
|
7885
8276
|
});
|
|
7886
|
-
const
|
|
8277
|
+
const spinner = startSpinner(SPINNER_MESSAGES.docs, !options.json);
|
|
8278
|
+
const result = await deps.packageIntelligenceService.listPackageDocs(build.params).finally(() => spinner.stop());
|
|
7887
8279
|
const payload = buildListPackageDocsSuccessPayload(result, {
|
|
7888
8280
|
limitExplicit: build.limitExplicit,
|
|
7889
8281
|
afterExplicit: build.afterExplicit,
|
|
@@ -7953,7 +8345,8 @@ async function docsReadAction(pageId, options, deps) {
|
|
|
7953
8345
|
}
|
|
7954
8346
|
const range = options.lines ? parseLinesOption(options.lines) : undefined;
|
|
7955
8347
|
const build = buildReadPackageDocParams({ pageId });
|
|
7956
|
-
const
|
|
8348
|
+
const spinner = startSpinner(SPINNER_MESSAGES.docs, !options.json);
|
|
8349
|
+
const result = await deps.packageIntelligenceService.readPackageDoc(build.params).finally(() => spinner.stop());
|
|
7957
8350
|
const payload = buildReadPackageDocSuccessPayload(result, build.params.pageId, range);
|
|
7958
8351
|
if (options.json) {
|
|
7959
8352
|
console.log(JSON.stringify(payload));
|
|
@@ -8006,7 +8399,7 @@ async function registerDocsCommandGroup(program, options = {}) {
|
|
|
8006
8399
|
if (!registration.shouldRegister) {
|
|
8007
8400
|
return;
|
|
8008
8401
|
}
|
|
8009
|
-
const docsCommand = program.command("docs").summary("Browse and read
|
|
8402
|
+
const docsCommand = program.command("docs").summary("Browse and read package documentation").description("Browse and read package documentation across hosted docs and repository-backed docs. Docs are mixed by default; entries are source-badged and repo-backed pages also expose exact file follow-up metadata.");
|
|
8010
8403
|
registerDocsListCommand(docsCommand);
|
|
8011
8404
|
registerDocsReadCommand(docsCommand);
|
|
8012
8405
|
}
|
|
@@ -8019,12 +8412,13 @@ async function exampleAction(query, options, deps) {
|
|
|
8019
8412
|
}
|
|
8020
8413
|
requireAuth(deps);
|
|
8021
8414
|
try {
|
|
8415
|
+
const spinner = startSpinner(SPINNER_MESSAGES.example, !options.json);
|
|
8022
8416
|
const result = await deps.githitsService.search({
|
|
8023
8417
|
query,
|
|
8024
8418
|
language: options.lang,
|
|
8025
8419
|
licenseMode: options.license,
|
|
8026
8420
|
includeExplanation: options.explain
|
|
8027
|
-
});
|
|
8421
|
+
}).finally(() => spinner.stop());
|
|
8028
8422
|
if (options.json) {
|
|
8029
8423
|
const solutionId = extractSolutionId(result);
|
|
8030
8424
|
const payload = solutionId ? { result, solution_id: solutionId } : { result };
|
|
@@ -8059,7 +8453,7 @@ Examples:
|
|
|
8059
8453
|
githits example "react hooks patterns" -l typescript --explain
|
|
8060
8454
|
githits example "react hooks patterns" -l typescript --json`;
|
|
8061
8455
|
function registerExampleCommand(program) {
|
|
8062
|
-
program.command("example").summary("
|
|
8456
|
+
program.command("example").summary("Find real-world implementations from open-source code").description(EXAMPLE_DESCRIPTION).argument("<query>", "Natural language example-search query").option("-l, --lang <language>", "Optional programming language; omitted values are inferred by GitHits").addOption(new Option("--license <mode>", "License filter mode").choices(["strict", "yolo", "custom"]).default(undefined)).option("--explain", "Include AI-generated explanation").option("--json", "Output as JSON for piping").action(async (query, options) => {
|
|
8063
8457
|
try {
|
|
8064
8458
|
const deps = await loadContainer();
|
|
8065
8459
|
await exampleAction(query, options, deps);
|
|
@@ -8071,7 +8465,7 @@ function registerExampleCommand(program) {
|
|
|
8071
8465
|
});
|
|
8072
8466
|
}
|
|
8073
8467
|
async function loadContainer() {
|
|
8074
|
-
const { createContainer: createContainer2 } = await import("./shared/chunk-
|
|
8468
|
+
const { createContainer: createContainer2 } = await import("./shared/chunk-70bn2pmx.js");
|
|
8075
8469
|
return createContainer2();
|
|
8076
8470
|
}
|
|
8077
8471
|
// src/commands/feedback.ts
|
|
@@ -8119,7 +8513,7 @@ Examples:
|
|
|
8119
8513
|
githits feedback --accept --tool code_grep -m "regex is fast on npm:lodash"
|
|
8120
8514
|
githits feedback --reject --tool search -m "missing kotlin support"`;
|
|
8121
8515
|
function registerFeedbackCommand(program) {
|
|
8122
|
-
program.command("feedback").summary("Submit feedback
|
|
8516
|
+
program.command("feedback").summary("Submit feedback about GitHits results or experience").description(FEEDBACK_DESCRIPTION).argument("[solution_id]", "Solution ID from a prior 'githits example' result (omit for generic feedback)").addOption(new Option2("--accept", "Mark as helpful").conflicts("reject")).addOption(new Option2("--reject", "Mark as unhelpful").conflicts("accept")).option("-m, --message <text>", "Feedback explanation").option("--tool <name>", "Command or MCP tool name being rated").option("--json", "Output as JSON for piping").action(async (solutionId, options) => {
|
|
8123
8517
|
try {
|
|
8124
8518
|
const deps = await createContainer();
|
|
8125
8519
|
await feedbackAction(solutionId, options, deps);
|
|
@@ -8362,17 +8756,11 @@ function removeServerConfig(existingContent, serversKey, serverName) {
|
|
|
8362
8756
|
`
|
|
8363
8757
|
};
|
|
8364
8758
|
}
|
|
8365
|
-
function
|
|
8366
|
-
if (config.method === "
|
|
8367
|
-
return config.
|
|
8759
|
+
function formatUninstallPreview(config) {
|
|
8760
|
+
if (config.method === "composite") {
|
|
8761
|
+
return config.steps.map(({ step }) => formatUninstallPreview(step)).join(`
|
|
8368
8762
|
`);
|
|
8369
8763
|
}
|
|
8370
|
-
const snippet = JSON.stringify({ [config.serverName]: config.serverConfig }, null, 2);
|
|
8371
|
-
return `Will add to ${config.configPath}:
|
|
8372
|
-
|
|
8373
|
-
${snippet}`;
|
|
8374
|
-
}
|
|
8375
|
-
function formatUninstallPreview(config) {
|
|
8376
8764
|
if (config.method === "cli") {
|
|
8377
8765
|
return config.commands.map((cmd) => `Will run: ${cmd.command} ${cmd.args.join(" ")}`).join(`
|
|
8378
8766
|
`);
|
|
@@ -8433,6 +8821,26 @@ async function getConfigUninstallCheckStatus(config, fs) {
|
|
|
8433
8821
|
};
|
|
8434
8822
|
}
|
|
8435
8823
|
}
|
|
8824
|
+
async function isSetupAlreadyConfigured(config, fs, execService) {
|
|
8825
|
+
if (config.method === "config-file") {
|
|
8826
|
+
return isAlreadyConfigured(config, fs);
|
|
8827
|
+
}
|
|
8828
|
+
if (config.method === "cli") {
|
|
8829
|
+
if (!config.checkCommand) {
|
|
8830
|
+
return false;
|
|
8831
|
+
}
|
|
8832
|
+
return isCliAlreadyConfigured(config.checkCommand, execService);
|
|
8833
|
+
}
|
|
8834
|
+
for (const step of config.steps) {
|
|
8835
|
+
if (!await isSetupAlreadyConfigured(step, fs, execService)) {
|
|
8836
|
+
return false;
|
|
8837
|
+
}
|
|
8838
|
+
}
|
|
8839
|
+
return true;
|
|
8840
|
+
}
|
|
8841
|
+
async function isCliAlreadyConfigured(check, execService) {
|
|
8842
|
+
return await getCliCheckStatus(check, execService) === "configured";
|
|
8843
|
+
}
|
|
8436
8844
|
async function getCliCheckStatus(check, execService) {
|
|
8437
8845
|
try {
|
|
8438
8846
|
const result = await execService.exec(check.command, check.args);
|
|
@@ -8463,6 +8871,7 @@ var ALREADY_EXISTS_PATTERNS = [
|
|
|
8463
8871
|
var ALREADY_ABSENT_PATTERNS = [
|
|
8464
8872
|
/(?:plugin|extension|server|mcp server)\s+["']?githits["']?\s+(?:was\s+)?not\s+found/i,
|
|
8465
8873
|
/["']?githits["']?\s+(?:plugin|extension|server)?\s*(?:does\s+not\s+exist|is\s+not\s+installed|not\s+installed)/i,
|
|
8874
|
+
/(?:package\s+)?["']?pi-mcp-adapter["']?\s+(?:is\s+)?not\s+installed/i,
|
|
8466
8875
|
/unknown\s+(?:plugin|extension|server)\s+["']?githits["']?/i,
|
|
8467
8876
|
/marketplace\s+["']?githits-plugins["']?\s+(?:was\s+)?not\s+found/i
|
|
8468
8877
|
];
|
|
@@ -8563,8 +8972,7 @@ async function executeCliUninstall(uninstall, execService) {
|
|
|
8563
8972
|
let anyRemoved = false;
|
|
8564
8973
|
let anyNotConfigured = false;
|
|
8565
8974
|
const warnings = [];
|
|
8566
|
-
for (
|
|
8567
|
-
const cmd = uninstall.commands[index];
|
|
8975
|
+
for (const cmd of uninstall.commands) {
|
|
8568
8976
|
const result = await executeCliUninstallCommand(cmd, execService);
|
|
8569
8977
|
if (result.status === "failed") {
|
|
8570
8978
|
if (anyRemoved) {
|
|
@@ -8599,6 +9007,49 @@ async function executeCliUninstall(uninstall, execService) {
|
|
|
8599
9007
|
}
|
|
8600
9008
|
return { status: "removed", message: "Removed successfully" };
|
|
8601
9009
|
}
|
|
9010
|
+
async function executeCompositeUninstall(uninstall, fs, execService) {
|
|
9011
|
+
let anyRemoved = false;
|
|
9012
|
+
let anyNotConfigured = false;
|
|
9013
|
+
const warnings = [];
|
|
9014
|
+
for (const { step, failureMode } of uninstall.steps) {
|
|
9015
|
+
const result = await executeUninstallStep(step, fs, execService);
|
|
9016
|
+
if (result.status === "removed") {
|
|
9017
|
+
anyRemoved = true;
|
|
9018
|
+
warnings.push(...result.warnings ?? []);
|
|
9019
|
+
continue;
|
|
9020
|
+
}
|
|
9021
|
+
if (result.status === "not_configured") {
|
|
9022
|
+
if (anyRemoved) {
|
|
9023
|
+
warnings.push(result.message);
|
|
9024
|
+
} else {
|
|
9025
|
+
anyNotConfigured = true;
|
|
9026
|
+
}
|
|
9027
|
+
continue;
|
|
9028
|
+
}
|
|
9029
|
+
if (failureMode === "best-effort" && anyRemoved) {
|
|
9030
|
+
warnings.push(result.message);
|
|
9031
|
+
continue;
|
|
9032
|
+
}
|
|
9033
|
+
return result;
|
|
9034
|
+
}
|
|
9035
|
+
if (anyRemoved) {
|
|
9036
|
+
return {
|
|
9037
|
+
status: "removed",
|
|
9038
|
+
message: "Removed successfully",
|
|
9039
|
+
warnings: warnings.length > 0 ? warnings : undefined
|
|
9040
|
+
};
|
|
9041
|
+
}
|
|
9042
|
+
if (anyNotConfigured) {
|
|
9043
|
+
return {
|
|
9044
|
+
status: "not_configured",
|
|
9045
|
+
message: "GitHits not configured"
|
|
9046
|
+
};
|
|
9047
|
+
}
|
|
9048
|
+
return { status: "not_configured", message: "GitHits not configured" };
|
|
9049
|
+
}
|
|
9050
|
+
async function executeUninstallStep(step, fs, execService) {
|
|
9051
|
+
return step.method === "cli" ? executeCliUninstall(step, execService) : executeConfigFileUninstall(step, fs);
|
|
9052
|
+
}
|
|
8602
9053
|
async function executeConfigFileSetup(setup, fs) {
|
|
8603
9054
|
try {
|
|
8604
9055
|
const parentDir = fs.getDirname(setup.configPath);
|
|
@@ -8642,6 +9093,26 @@ async function executeConfigFileSetup(setup, fs) {
|
|
|
8642
9093
|
};
|
|
8643
9094
|
}
|
|
8644
9095
|
}
|
|
9096
|
+
async function executeCompositeSetup(setup, fs, execService) {
|
|
9097
|
+
let executedAny = false;
|
|
9098
|
+
for (const step of setup.steps) {
|
|
9099
|
+
if (await isSetupAlreadyConfigured(step, fs, execService)) {
|
|
9100
|
+
continue;
|
|
9101
|
+
}
|
|
9102
|
+
executedAny = true;
|
|
9103
|
+
const result = step.method === "cli" ? await executeCliSetup(step, execService) : await executeConfigFileSetup(step, fs);
|
|
9104
|
+
if (result.status === "failed") {
|
|
9105
|
+
return result;
|
|
9106
|
+
}
|
|
9107
|
+
}
|
|
9108
|
+
if (!executedAny) {
|
|
9109
|
+
return {
|
|
9110
|
+
status: "already_configured",
|
|
9111
|
+
message: "GitHits already configured"
|
|
9112
|
+
};
|
|
9113
|
+
}
|
|
9114
|
+
return { status: "success", message: "Configured successfully" };
|
|
9115
|
+
}
|
|
8645
9116
|
async function executeConfigFileUninstall(setup, fs) {
|
|
8646
9117
|
try {
|
|
8647
9118
|
let existingContent = "";
|
|
@@ -8728,6 +9199,25 @@ function getOpenCodeConfigDir(fs) {
|
|
|
8728
9199
|
}
|
|
8729
9200
|
return fs.joinPath(fs.getHomeDir(), ".config", "opencode");
|
|
8730
9201
|
}
|
|
9202
|
+
function expandHomePath(fs, path) {
|
|
9203
|
+
if (path === "~") {
|
|
9204
|
+
return fs.getHomeDir();
|
|
9205
|
+
}
|
|
9206
|
+
if (path.startsWith("~/")) {
|
|
9207
|
+
return fs.joinPath(fs.getHomeDir(), path.slice(2));
|
|
9208
|
+
}
|
|
9209
|
+
return path;
|
|
9210
|
+
}
|
|
9211
|
+
function getPiAgentDir(fs) {
|
|
9212
|
+
const configuredDir = process.env.PI_CODING_AGENT_DIR?.trim();
|
|
9213
|
+
if (configuredDir) {
|
|
9214
|
+
return expandHomePath(fs, configuredDir);
|
|
9215
|
+
}
|
|
9216
|
+
return fs.joinPath(fs.getHomeDir(), ".pi", "agent");
|
|
9217
|
+
}
|
|
9218
|
+
function getPiMcpConfigPath(fs) {
|
|
9219
|
+
return fs.joinPath(getPiAgentDir(fs), "mcp.json");
|
|
9220
|
+
}
|
|
8731
9221
|
function getOpenCodeDesktopDetectPaths(fs) {
|
|
8732
9222
|
const userDataRoot = getUserDataRoot(fs);
|
|
8733
9223
|
return [
|
|
@@ -8746,6 +9236,57 @@ async function isExecutableAvailable(exec, executable) {
|
|
|
8746
9236
|
return false;
|
|
8747
9237
|
}
|
|
8748
9238
|
}
|
|
9239
|
+
async function resolveExecutableFromPath(exec, executable) {
|
|
9240
|
+
return isExecutableAvailable(exec, executable);
|
|
9241
|
+
}
|
|
9242
|
+
var PI_GLOBAL_BIN_PROBES = [
|
|
9243
|
+
{ command: "npm", args: ["prefix", "-g"], output: "prefix" },
|
|
9244
|
+
{ command: "pnpm", args: ["bin", "-g"], output: "binDir" },
|
|
9245
|
+
{ command: "bun", args: ["pm", "bin", "-g"], output: "binDir" }
|
|
9246
|
+
];
|
|
9247
|
+
var PI_ADAPTER_CONFIGURED_PATTERN = /(?:^|\s|:)(?:npm:)?pi-mcp-adapter(?:[\s@:]|$)/i;
|
|
9248
|
+
function getPiExecutableNames() {
|
|
9249
|
+
return process.platform === "win32" ? ["pi.cmd", "pi.exe", "pi"] : ["pi"];
|
|
9250
|
+
}
|
|
9251
|
+
async function runGlobalBinProbe(exec, probe) {
|
|
9252
|
+
try {
|
|
9253
|
+
const result = await exec.exec(probe.command, [...probe.args]);
|
|
9254
|
+
if (result.exitCode !== 0) {
|
|
9255
|
+
return null;
|
|
9256
|
+
}
|
|
9257
|
+
const probePath = result.stdout.split(/\r?\n/).map((line) => line.trim()).find((line) => line.length > 0);
|
|
9258
|
+
if (!probePath) {
|
|
9259
|
+
return null;
|
|
9260
|
+
}
|
|
9261
|
+
if (probe.output === "prefix" && process.platform !== "win32") {
|
|
9262
|
+
return fsJoinPathLike(probePath, "bin");
|
|
9263
|
+
}
|
|
9264
|
+
return probePath;
|
|
9265
|
+
} catch {
|
|
9266
|
+
return null;
|
|
9267
|
+
}
|
|
9268
|
+
}
|
|
9269
|
+
function fsJoinPathLike(base, child) {
|
|
9270
|
+
return base.endsWith("/") ? `${base}${child}` : `${base}/${child}`;
|
|
9271
|
+
}
|
|
9272
|
+
async function detectPiExecutable(exec, fs) {
|
|
9273
|
+
if (await resolveExecutableFromPath(exec, "pi")) {
|
|
9274
|
+
return { command: "pi" };
|
|
9275
|
+
}
|
|
9276
|
+
for (const probe of PI_GLOBAL_BIN_PROBES) {
|
|
9277
|
+
const binDir = await runGlobalBinProbe(exec, probe);
|
|
9278
|
+
if (!binDir) {
|
|
9279
|
+
continue;
|
|
9280
|
+
}
|
|
9281
|
+
for (const executableName of getPiExecutableNames()) {
|
|
9282
|
+
const candidate = fs.joinPath(binDir, executableName);
|
|
9283
|
+
if (await fs.exists(candidate)) {
|
|
9284
|
+
return { command: candidate };
|
|
9285
|
+
}
|
|
9286
|
+
}
|
|
9287
|
+
}
|
|
9288
|
+
return null;
|
|
9289
|
+
}
|
|
8749
9290
|
var claudeCode = {
|
|
8750
9291
|
name: "Claude Code",
|
|
8751
9292
|
id: "claude-code",
|
|
@@ -8885,6 +9426,76 @@ var codexCli = {
|
|
|
8885
9426
|
]
|
|
8886
9427
|
})
|
|
8887
9428
|
};
|
|
9429
|
+
var pi = {
|
|
9430
|
+
name: "Pi",
|
|
9431
|
+
id: "pi",
|
|
9432
|
+
detectionMethod: "binary",
|
|
9433
|
+
setupMethod: "composite",
|
|
9434
|
+
detectCommand: detectPiExecutable,
|
|
9435
|
+
getSetupConfig: (fs, context) => {
|
|
9436
|
+
const piCommand = context?.command ?? "pi";
|
|
9437
|
+
return {
|
|
9438
|
+
method: "composite",
|
|
9439
|
+
steps: [
|
|
9440
|
+
{
|
|
9441
|
+
method: "cli",
|
|
9442
|
+
commands: [
|
|
9443
|
+
{
|
|
9444
|
+
command: piCommand,
|
|
9445
|
+
args: ["install", "npm:pi-mcp-adapter"]
|
|
9446
|
+
}
|
|
9447
|
+
],
|
|
9448
|
+
checkCommand: {
|
|
9449
|
+
command: piCommand,
|
|
9450
|
+
args: ["list"],
|
|
9451
|
+
configuredPattern: PI_ADAPTER_CONFIGURED_PATTERN
|
|
9452
|
+
}
|
|
9453
|
+
},
|
|
9454
|
+
{
|
|
9455
|
+
method: "config-file",
|
|
9456
|
+
configPath: getPiMcpConfigPath(fs),
|
|
9457
|
+
serversKey: "mcpServers",
|
|
9458
|
+
serverName: GITHITS_SERVER_NAME,
|
|
9459
|
+
serverConfig: {
|
|
9460
|
+
command: GITHITS_MCP_COMMAND,
|
|
9461
|
+
args: [...GITHITS_MCP_ARGS],
|
|
9462
|
+
lifecycle: "eager"
|
|
9463
|
+
}
|
|
9464
|
+
}
|
|
9465
|
+
]
|
|
9466
|
+
};
|
|
9467
|
+
},
|
|
9468
|
+
getUninstallConfig: (fs, context) => {
|
|
9469
|
+
const piCommand = context?.command ?? "pi";
|
|
9470
|
+
return {
|
|
9471
|
+
method: "composite",
|
|
9472
|
+
steps: [
|
|
9473
|
+
{
|
|
9474
|
+
failureMode: "required",
|
|
9475
|
+
step: {
|
|
9476
|
+
method: "config-file",
|
|
9477
|
+
configPath: getPiMcpConfigPath(fs),
|
|
9478
|
+
serversKey: "mcpServers",
|
|
9479
|
+
serverName: GITHITS_SERVER_NAME,
|
|
9480
|
+
serverConfig: {}
|
|
9481
|
+
}
|
|
9482
|
+
},
|
|
9483
|
+
{
|
|
9484
|
+
failureMode: "required",
|
|
9485
|
+
step: {
|
|
9486
|
+
method: "cli",
|
|
9487
|
+
commands: [
|
|
9488
|
+
{
|
|
9489
|
+
command: piCommand,
|
|
9490
|
+
args: ["remove", "npm:pi-mcp-adapter"]
|
|
9491
|
+
}
|
|
9492
|
+
]
|
|
9493
|
+
}
|
|
9494
|
+
}
|
|
9495
|
+
]
|
|
9496
|
+
};
|
|
9497
|
+
}
|
|
9498
|
+
};
|
|
8888
9499
|
var vscode = {
|
|
8889
9500
|
name: "VS Code / Copilot",
|
|
8890
9501
|
id: "vscode",
|
|
@@ -9009,82 +9620,110 @@ var agentDefinitions = [
|
|
|
9009
9620
|
cline,
|
|
9010
9621
|
claudeDesktop,
|
|
9011
9622
|
codexCli,
|
|
9623
|
+
pi,
|
|
9012
9624
|
geminiCli,
|
|
9013
9625
|
googleAntigravity,
|
|
9014
9626
|
openCode
|
|
9015
9627
|
];
|
|
9016
|
-
async function
|
|
9017
|
-
|
|
9018
|
-
|
|
9019
|
-
|
|
9020
|
-
|
|
9021
|
-
|
|
9022
|
-
|
|
9023
|
-
|
|
9024
|
-
|
|
9025
|
-
try {
|
|
9026
|
-
detected = await agent.detectBinary(execService);
|
|
9027
|
-
} catch {
|
|
9028
|
-
detected = false;
|
|
9628
|
+
async function scanSingleAgent(agent, fs, execService) {
|
|
9629
|
+
let detected = false;
|
|
9630
|
+
let setupContext;
|
|
9631
|
+
if (agent.detectCommand) {
|
|
9632
|
+
try {
|
|
9633
|
+
const resolvedCommand = await agent.detectCommand(execService, fs);
|
|
9634
|
+
if (resolvedCommand) {
|
|
9635
|
+
detected = true;
|
|
9636
|
+
setupContext = { command: resolvedCommand.command };
|
|
9029
9637
|
}
|
|
9030
|
-
}
|
|
9031
|
-
|
|
9032
|
-
|
|
9033
|
-
|
|
9034
|
-
|
|
9035
|
-
|
|
9036
|
-
|
|
9638
|
+
} catch {
|
|
9639
|
+
detected = false;
|
|
9640
|
+
}
|
|
9641
|
+
} else if (agent.detectionMethod === "binary" && agent.detectBinary) {
|
|
9642
|
+
try {
|
|
9643
|
+
detected = await agent.detectBinary(execService);
|
|
9644
|
+
} catch {
|
|
9645
|
+
detected = false;
|
|
9646
|
+
}
|
|
9647
|
+
} else if (agent.detectionMethod === "path" && agent.detectPaths) {
|
|
9648
|
+
const paths = agent.detectPaths(fs);
|
|
9649
|
+
for (const path of paths) {
|
|
9650
|
+
if (await fs.isDirectory(path)) {
|
|
9651
|
+
detected = true;
|
|
9652
|
+
break;
|
|
9037
9653
|
}
|
|
9038
|
-
}
|
|
9039
|
-
|
|
9040
|
-
|
|
9041
|
-
|
|
9042
|
-
|
|
9043
|
-
|
|
9044
|
-
|
|
9045
|
-
|
|
9046
|
-
|
|
9654
|
+
}
|
|
9655
|
+
} else if (agent.detectionMethod === "hybrid") {
|
|
9656
|
+
let binaryDetected = false;
|
|
9657
|
+
let pathDetected = false;
|
|
9658
|
+
if (agent.detectBinary) {
|
|
9659
|
+
try {
|
|
9660
|
+
binaryDetected = await agent.detectBinary(execService);
|
|
9661
|
+
} catch {
|
|
9662
|
+
binaryDetected = false;
|
|
9047
9663
|
}
|
|
9048
|
-
|
|
9049
|
-
|
|
9050
|
-
|
|
9051
|
-
|
|
9052
|
-
|
|
9053
|
-
|
|
9054
|
-
|
|
9664
|
+
}
|
|
9665
|
+
if (!binaryDetected && agent.detectPaths) {
|
|
9666
|
+
const paths = agent.detectPaths(fs);
|
|
9667
|
+
for (const path of paths) {
|
|
9668
|
+
if (await fs.isDirectory(path)) {
|
|
9669
|
+
pathDetected = true;
|
|
9670
|
+
break;
|
|
9055
9671
|
}
|
|
9056
9672
|
}
|
|
9057
|
-
detected = binaryDetected || pathDetected;
|
|
9058
9673
|
}
|
|
9059
|
-
|
|
9060
|
-
|
|
9061
|
-
|
|
9674
|
+
detected = binaryDetected || pathDetected;
|
|
9675
|
+
}
|
|
9676
|
+
if (!detected) {
|
|
9677
|
+
return { status: "not_detected", agent };
|
|
9678
|
+
}
|
|
9679
|
+
const config = agent.getSetupConfig(fs, setupContext);
|
|
9680
|
+
const scannedAgent = {
|
|
9681
|
+
...agent,
|
|
9682
|
+
resolvedSetupConfig: config,
|
|
9683
|
+
resolvedSetupContext: setupContext
|
|
9684
|
+
};
|
|
9685
|
+
if (agent.id === "gemini-cli" && config.method === "cli") {
|
|
9686
|
+
if (!config.checkCommand) {
|
|
9687
|
+
return { status: "needs_setup", agent: scannedAgent };
|
|
9062
9688
|
}
|
|
9063
|
-
|
|
9064
|
-
|
|
9065
|
-
|
|
9066
|
-
|
|
9067
|
-
|
|
9068
|
-
|
|
9069
|
-
|
|
9689
|
+
const checkStatus = await getCliCheckStatus(config.checkCommand, execService);
|
|
9690
|
+
let configured = checkStatus === "configured";
|
|
9691
|
+
if (!configured && checkStatus === "probe_failed") {
|
|
9692
|
+
configured = await isGeminiExtensionInstalledFromFilesystem(fs);
|
|
9693
|
+
}
|
|
9694
|
+
return {
|
|
9695
|
+
status: configured ? "already_configured" : "needs_setup",
|
|
9696
|
+
agent: scannedAgent
|
|
9697
|
+
};
|
|
9698
|
+
}
|
|
9699
|
+
if (await isSetupAlreadyConfigured(config, fs, execService)) {
|
|
9700
|
+
return { status: "already_configured", agent: scannedAgent };
|
|
9701
|
+
}
|
|
9702
|
+
return { status: "needs_setup", agent: scannedAgent };
|
|
9703
|
+
}
|
|
9704
|
+
async function scanAgents(definitions, fs, execService, options = {}) {
|
|
9705
|
+
const result = {
|
|
9706
|
+
needsSetup: [],
|
|
9707
|
+
alreadyConfigured: [],
|
|
9708
|
+
notDetected: []
|
|
9709
|
+
};
|
|
9710
|
+
let completed = 0;
|
|
9711
|
+
const outcomes = await Promise.all(definitions.map((agent) => scanSingleAgent(agent, fs, execService).then((outcome) => {
|
|
9712
|
+
completed += 1;
|
|
9713
|
+
options.onProgress?.({
|
|
9714
|
+
completed,
|
|
9715
|
+
total: definitions.length,
|
|
9716
|
+
agent: outcome.agent
|
|
9717
|
+
});
|
|
9718
|
+
return outcome;
|
|
9719
|
+
})));
|
|
9720
|
+
for (const outcome of outcomes) {
|
|
9721
|
+
if (outcome.status === "already_configured") {
|
|
9722
|
+
result.alreadyConfigured.push(outcome.agent);
|
|
9723
|
+
} else if (outcome.status === "needs_setup") {
|
|
9724
|
+
result.needsSetup.push(outcome.agent);
|
|
9070
9725
|
} else {
|
|
9071
|
-
|
|
9072
|
-
if (config.method === "cli" && config.checkCommand) {
|
|
9073
|
-
const checkStatus = await getCliCheckStatus(config.checkCommand, execService);
|
|
9074
|
-
let configured = checkStatus === "configured";
|
|
9075
|
-
if (!configured && agent.id === "gemini-cli") {
|
|
9076
|
-
if (checkStatus === "probe_failed") {
|
|
9077
|
-
configured = await isGeminiExtensionInstalledFromFilesystem(fs);
|
|
9078
|
-
}
|
|
9079
|
-
}
|
|
9080
|
-
if (configured) {
|
|
9081
|
-
result.alreadyConfigured.push(agent);
|
|
9082
|
-
} else {
|
|
9083
|
-
result.needsSetup.push(agent);
|
|
9084
|
-
}
|
|
9085
|
-
} else {
|
|
9086
|
-
result.needsSetup.push(agent);
|
|
9087
|
-
}
|
|
9726
|
+
result.notDetected.push(outcome.agent);
|
|
9088
9727
|
}
|
|
9089
9728
|
}
|
|
9090
9729
|
return result;
|
|
@@ -9094,14 +9733,440 @@ class ExitPromptError extends Error {
|
|
|
9094
9733
|
name = "ExitPromptError";
|
|
9095
9734
|
}
|
|
9096
9735
|
// src/commands/init/init.ts
|
|
9736
|
+
function createInitLoginOutput() {
|
|
9737
|
+
return {
|
|
9738
|
+
write: (message) => {
|
|
9739
|
+
const lines = message.replace(/\n$/, "").split(`
|
|
9740
|
+
`);
|
|
9741
|
+
for (const line of lines) {
|
|
9742
|
+
console.log(line.length > 0 ? ` ${line}` : "");
|
|
9743
|
+
}
|
|
9744
|
+
}
|
|
9745
|
+
};
|
|
9746
|
+
}
|
|
9747
|
+
function getResolvedSetupConfig(agent, fileSystemService) {
|
|
9748
|
+
return agent.resolvedSetupConfig ?? agent.getSetupConfig(fileSystemService, agent.resolvedSetupContext);
|
|
9749
|
+
}
|
|
9750
|
+
function getPiConfigFileUninstall(agent, fileSystemService) {
|
|
9751
|
+
if (agent.id !== "pi") {
|
|
9752
|
+
return null;
|
|
9753
|
+
}
|
|
9754
|
+
const uninstallConfig = agent.getUninstallConfig?.(fileSystemService);
|
|
9755
|
+
if (uninstallConfig?.method !== "composite") {
|
|
9756
|
+
return null;
|
|
9757
|
+
}
|
|
9758
|
+
const configStep = uninstallConfig.steps.map(({ step }) => step).find((step) => step.method === "config-file");
|
|
9759
|
+
return configStep ?? null;
|
|
9760
|
+
}
|
|
9761
|
+
function formatCommand(command, useColors) {
|
|
9762
|
+
return colorizeBrand(command, "secondary", useColors, { bold: true });
|
|
9763
|
+
}
|
|
9097
9764
|
function printReadyNextSteps() {
|
|
9098
|
-
console.log("
|
|
9765
|
+
console.log(" GitHits is now connected to your coding agents.");
|
|
9766
|
+
console.log();
|
|
9767
|
+
console.log(" Here are some examples of the new abilities that your agent just got:");
|
|
9768
|
+
console.log();
|
|
9769
|
+
console.log(" • Find usage examples");
|
|
9770
|
+
console.log(" -> “Find a real example of using Azure Speech SDK TranscribeDefinition”");
|
|
9771
|
+
console.log();
|
|
9772
|
+
console.log(" • Search, grep, list files, and read exact lines in any repo or package to gather information");
|
|
9773
|
+
console.log(" -> “How does Next.js implement route prefetching internally?”");
|
|
9774
|
+
console.log();
|
|
9775
|
+
console.log(" • Inspect dependency versions, changelogs, and upgrade changes");
|
|
9776
|
+
console.log(" -> “What changed between pydantic-ai 1.95 and 1.99?”");
|
|
9777
|
+
console.log();
|
|
9778
|
+
console.log(" Open a new coding agent session and try out one of the above.");
|
|
9779
|
+
console.log();
|
|
9780
|
+
console.log(' In your normal workflow, your agent will call GitHits automatically depending on the task, but you can prompt it to use GitHits explicitly by adding "use GitHits".');
|
|
9781
|
+
console.log();
|
|
9782
|
+
console.log(" See docs for more use cases and trigger guides: https://docs.githits.com");
|
|
9783
|
+
}
|
|
9784
|
+
function printAuthRequiredNextSteps(useColors) {
|
|
9785
|
+
console.log(" GitHits MCP is configured, but sign-in is still needed.");
|
|
9786
|
+
console.log();
|
|
9787
|
+
console.log(" Sign in when you're ready:");
|
|
9788
|
+
console.log(` ${formatCommand("npx githits@latest login", useColors)}`);
|
|
9789
|
+
}
|
|
9790
|
+
function printAuthNotCheckedNextSteps(useColors) {
|
|
9791
|
+
console.log(" GitHits MCP is configured. Sign-in was not checked.");
|
|
9792
|
+
console.log();
|
|
9793
|
+
console.log(" If your agent asks you to sign in, run:");
|
|
9794
|
+
console.log(` ${formatCommand("npx githits@latest login", useColors)}`);
|
|
9795
|
+
}
|
|
9796
|
+
var GITHITS_ASCII_LOGO = String.raw`
|
|
9797
|
+
____ _ _ _ _ _ _
|
|
9798
|
+
/ ___(_) |_| | | (_) |_ ___
|
|
9799
|
+
| | _| | __| |_| | | __/ __|
|
|
9800
|
+
| |_| | | |_| _ | | |_\__ \
|
|
9801
|
+
\____|_|\__|_| |_|_|\__|___/
|
|
9802
|
+
`;
|
|
9803
|
+
var LOGO_GRADIENT = [
|
|
9804
|
+
{
|
|
9805
|
+
until: 13,
|
|
9806
|
+
color: {
|
|
9807
|
+
hex: "#FF4FAE",
|
|
9808
|
+
rgb: [255, 79, 174],
|
|
9809
|
+
ansi256: 205,
|
|
9810
|
+
ansi16: "magenta"
|
|
9811
|
+
}
|
|
9812
|
+
},
|
|
9813
|
+
{
|
|
9814
|
+
until: 19,
|
|
9815
|
+
color: {
|
|
9816
|
+
hex: "#FF5D8E",
|
|
9817
|
+
rgb: [255, 93, 142],
|
|
9818
|
+
ansi256: 204,
|
|
9819
|
+
ansi16: "magenta"
|
|
9820
|
+
}
|
|
9821
|
+
},
|
|
9822
|
+
{
|
|
9823
|
+
until: 21,
|
|
9824
|
+
color: {
|
|
9825
|
+
hex: "#FF6B6F",
|
|
9826
|
+
rgb: [255, 107, 111],
|
|
9827
|
+
ansi256: 203,
|
|
9828
|
+
ansi16: "red"
|
|
9829
|
+
}
|
|
9830
|
+
},
|
|
9831
|
+
{
|
|
9832
|
+
until: 25,
|
|
9833
|
+
color: { hex: "#FF794F", rgb: [255, 121, 79], ansi256: 209, ansi16: "red" }
|
|
9834
|
+
},
|
|
9835
|
+
{
|
|
9836
|
+
until: 30,
|
|
9837
|
+
color: {
|
|
9838
|
+
hex: "#FF872F",
|
|
9839
|
+
rgb: [255, 135, 47],
|
|
9840
|
+
ansi256: 208,
|
|
9841
|
+
ansi16: "yellow"
|
|
9842
|
+
}
|
|
9843
|
+
}
|
|
9844
|
+
];
|
|
9845
|
+
function colorizeLogo(logo, useColors) {
|
|
9846
|
+
if (!useColors) {
|
|
9847
|
+
return logo;
|
|
9848
|
+
}
|
|
9849
|
+
return logo.split(`
|
|
9850
|
+
`).map((line) => {
|
|
9851
|
+
if (line.length === 0) {
|
|
9852
|
+
return line;
|
|
9853
|
+
}
|
|
9854
|
+
let cursor2 = 0;
|
|
9855
|
+
let colored = "";
|
|
9856
|
+
for (const { until, color } of LOGO_GRADIENT) {
|
|
9857
|
+
if (cursor2 >= line.length) {
|
|
9858
|
+
break;
|
|
9859
|
+
}
|
|
9860
|
+
colored += colorizeTerminal(line.slice(cursor2, until), color, useColors);
|
|
9861
|
+
cursor2 = until;
|
|
9862
|
+
}
|
|
9863
|
+
return cursor2 < line.length ? colored + line.slice(cursor2) : colored;
|
|
9864
|
+
}).join(`
|
|
9865
|
+
`);
|
|
9866
|
+
}
|
|
9867
|
+
var INIT_INTENT_CHOICES = [
|
|
9868
|
+
{
|
|
9869
|
+
name: "Connect GitHits to my agent (Recommended)",
|
|
9870
|
+
value: "mcp",
|
|
9871
|
+
description: "Install the local GitHits MCP server for your coding agents. Allows agents to seamlessly use GitHits."
|
|
9872
|
+
},
|
|
9873
|
+
{
|
|
9874
|
+
name: "Use Agent Skills instead",
|
|
9875
|
+
value: "skills",
|
|
9876
|
+
description: "Use Skills instead of the local MCP server."
|
|
9877
|
+
},
|
|
9878
|
+
{
|
|
9879
|
+
name: "Exit",
|
|
9880
|
+
value: "later",
|
|
9881
|
+
description: "Leave setup without making changes."
|
|
9882
|
+
}
|
|
9883
|
+
];
|
|
9884
|
+
var AUTH_RECOVERY_CHOICES = [
|
|
9885
|
+
{ name: "Retry sign in", value: "retry" },
|
|
9886
|
+
{
|
|
9887
|
+
name: "Configure MCP without signing in",
|
|
9888
|
+
value: "continue_without_auth",
|
|
9889
|
+
description: "Your agent will ask you to sign in before using GitHits."
|
|
9890
|
+
},
|
|
9891
|
+
{ name: "Cancel setup", value: "cancel" }
|
|
9892
|
+
];
|
|
9893
|
+
var AUTH_START_CHOICES = [
|
|
9894
|
+
{
|
|
9895
|
+
name: "Sign in now",
|
|
9896
|
+
value: "sign_in",
|
|
9897
|
+
description: "Open your browser and connect this CLI to GitHits."
|
|
9898
|
+
},
|
|
9899
|
+
{
|
|
9900
|
+
name: "Skip for now",
|
|
9901
|
+
value: "skip",
|
|
9902
|
+
description: "Finish MCP setup and sign in later with `githits login`."
|
|
9903
|
+
},
|
|
9904
|
+
{ name: "Cancel setup", value: "cancel" }
|
|
9905
|
+
];
|
|
9906
|
+
function printSection(index, title, useColors) {
|
|
9907
|
+
console.log();
|
|
9908
|
+
console.log(` ${colorizeBrand(`${index}. ${title}`, "primary", useColors, { bold: true })}`);
|
|
9909
|
+
console.log(` ${colorize("-".repeat(title.length + 3), "dim", useColors)}`);
|
|
9910
|
+
}
|
|
9911
|
+
function printTask(status, label, detail, useColors) {
|
|
9912
|
+
const suffix = detail ? ` ${colorize(detail, "dim", useColors)}` : "";
|
|
9913
|
+
if (status === "success") {
|
|
9914
|
+
console.log(` ${success(label, useColors)}${suffix}`);
|
|
9915
|
+
} else if (status === "failed") {
|
|
9916
|
+
console.log(` ${error(label, useColors)}${suffix}`);
|
|
9917
|
+
} else if (status === "warning") {
|
|
9918
|
+
console.log(` ${warning(label, useColors)}${suffix}`);
|
|
9919
|
+
} else {
|
|
9920
|
+
console.log(` ${colorize("○", "dim", useColors)} ${label}${suffix}`);
|
|
9921
|
+
}
|
|
9922
|
+
}
|
|
9923
|
+
function printInitIntro(useColors) {
|
|
9924
|
+
console.log(colorizeLogo(GITHITS_ASCII_LOGO, useColors));
|
|
9925
|
+
console.log(" Your agent can read your local codebase.");
|
|
9926
|
+
console.log();
|
|
9927
|
+
console.log(" GitHits lets it navigate the open-source code your app depends on.");
|
|
9928
|
+
console.log();
|
|
9929
|
+
console.log(` ${colorizeBrand("With GitHits, your agent can:", "primary", useColors)}`);
|
|
9930
|
+
console.log(" • Find implementation examples from open-source code, issues, discussions, and pull requests");
|
|
9931
|
+
console.log(" • Search, grep, list files, and read exact lines in any repo or package");
|
|
9932
|
+
console.log(" • Inspect dependency internals, versions, changelogs, and upgrade changes");
|
|
9933
|
+
console.log(" • Access package documentation");
|
|
9934
|
+
console.log();
|
|
9935
|
+
console.log(" No cloning or local indexing required. GitHits handles everything automatically.");
|
|
9936
|
+
console.log();
|
|
9937
|
+
console.log(" Works with Cursor, Claude Code, Codex, OpenCode, Pi, VS Code, Windsurf, and more.");
|
|
9938
|
+
console.log();
|
|
9939
|
+
console.log(" More info: https://docs.githits.com");
|
|
9940
|
+
console.log();
|
|
9941
|
+
}
|
|
9942
|
+
function printSkillsInstructions(useColors) {
|
|
9943
|
+
console.log(`
|
|
9944
|
+
Install GitHits Agent Skills:`);
|
|
9945
|
+
console.log();
|
|
9946
|
+
console.log(` ${formatCommand("npx skills add githits-com/githits-cli", useColors)}`);
|
|
9947
|
+
console.log();
|
|
9948
|
+
console.log(" During setup, choose where you want to enable GitHits.");
|
|
9949
|
+
console.log();
|
|
9950
|
+
console.log(" IMPORTANT: Use either Agent Skills or the local MCP server in the same");
|
|
9951
|
+
console.log(" coding tool, not both.");
|
|
9952
|
+
console.log();
|
|
9953
|
+
console.log(" Then sign in so your agent can use GitHits:");
|
|
9954
|
+
console.log();
|
|
9955
|
+
console.log(` ${formatCommand("npx githits@latest login", useColors)}`);
|
|
9956
|
+
console.log();
|
|
9957
|
+
}
|
|
9958
|
+
function startSafeInitScan(fileSystemService, execService, onProgress) {
|
|
9959
|
+
return scanAgents(agentDefinitions, fileSystemService, execService, {
|
|
9960
|
+
onProgress
|
|
9961
|
+
}).then((scan) => ({ ok: true, scan })).catch((error2) => ({
|
|
9962
|
+
ok: false,
|
|
9963
|
+
error: error2 instanceof Error ? error2 : new Error(String(error2))
|
|
9964
|
+
}));
|
|
9965
|
+
}
|
|
9966
|
+
function createScanProgressReporter(useColors) {
|
|
9967
|
+
if (!process.stdout.isTTY) {
|
|
9968
|
+
return { onProgress: () => {}, finish: () => {} };
|
|
9969
|
+
}
|
|
9970
|
+
let wrote = false;
|
|
9971
|
+
return {
|
|
9972
|
+
onProgress: (progress) => {
|
|
9973
|
+
const width = 20;
|
|
9974
|
+
const filled = Math.round(progress.completed / progress.total * width);
|
|
9975
|
+
const bar = `${colorizeBrand("#".repeat(filled), "primary", useColors)}${"-".repeat(width - filled)}`;
|
|
9976
|
+
const line = ` Scanning tools [${bar}] ${progress.completed}/${progress.total} ${progress.agent.name}`;
|
|
9977
|
+
process.stdout.write(`\r\x1B[2K${line}`);
|
|
9978
|
+
wrote = true;
|
|
9979
|
+
},
|
|
9980
|
+
finish: () => {
|
|
9981
|
+
if (wrote) {
|
|
9982
|
+
process.stdout.write("\r\x1B[2K");
|
|
9983
|
+
}
|
|
9984
|
+
}
|
|
9985
|
+
};
|
|
9986
|
+
}
|
|
9987
|
+
function createInstallTaskReporter(useColors) {
|
|
9988
|
+
if (!process.stdout.isTTY) {
|
|
9989
|
+
return {
|
|
9990
|
+
start: (label) => {
|
|
9991
|
+
printTask("skipped", label, "installing...", useColors);
|
|
9992
|
+
return () => {};
|
|
9993
|
+
}
|
|
9994
|
+
};
|
|
9995
|
+
}
|
|
9996
|
+
const frames = ["-", "\\", "|", "/"];
|
|
9997
|
+
return {
|
|
9998
|
+
start: (label) => {
|
|
9999
|
+
let frame = 0;
|
|
10000
|
+
const render = () => {
|
|
10001
|
+
const spinner = colorizeBrand(frames[frame % frames.length] ?? "-", "primary", useColors);
|
|
10002
|
+
frame += 1;
|
|
10003
|
+
process.stdout.write(`\r\x1B[2K ${spinner} ${label} installing...`);
|
|
10004
|
+
};
|
|
10005
|
+
render();
|
|
10006
|
+
const interval = setInterval(render, 80);
|
|
10007
|
+
return () => {
|
|
10008
|
+
clearInterval(interval);
|
|
10009
|
+
process.stdout.write("\r\x1B[2K");
|
|
10010
|
+
};
|
|
10011
|
+
}
|
|
10012
|
+
};
|
|
10013
|
+
}
|
|
10014
|
+
async function unwrapSafeScan(scanPromise) {
|
|
10015
|
+
const result = await scanPromise;
|
|
10016
|
+
if (!result.ok) {
|
|
10017
|
+
throw result.error;
|
|
10018
|
+
}
|
|
10019
|
+
return result.scan;
|
|
10020
|
+
}
|
|
10021
|
+
function formatAgentNames(agents) {
|
|
10022
|
+
if (agents.length === 0)
|
|
10023
|
+
return "none";
|
|
10024
|
+
if (agents.length === 1)
|
|
10025
|
+
return agents[0]?.name ?? "unknown";
|
|
10026
|
+
if (agents.length === 2) {
|
|
10027
|
+
return `${agents[0]?.name ?? "unknown"} and ${agents[1]?.name ?? "unknown"}`;
|
|
10028
|
+
}
|
|
10029
|
+
const names = agents.map((agent) => agent.name);
|
|
10030
|
+
return `${names.slice(0, -1).join(", ")}, and ${names[names.length - 1]}`;
|
|
10031
|
+
}
|
|
10032
|
+
function buildInitAgentChoices(scan) {
|
|
10033
|
+
return [
|
|
10034
|
+
...scan.needsSetup.map((agent) => ({
|
|
10035
|
+
name: `${agent.name} (detected)`,
|
|
10036
|
+
value: agent,
|
|
10037
|
+
checked: true
|
|
10038
|
+
})),
|
|
10039
|
+
...scan.alreadyConfigured.map((agent) => ({
|
|
10040
|
+
name: `${agent.name} (already configured)`,
|
|
10041
|
+
value: agent,
|
|
10042
|
+
disabled: "already configured"
|
|
10043
|
+
}))
|
|
10044
|
+
];
|
|
10045
|
+
}
|
|
10046
|
+
function printScanSummary(scan, useColors) {
|
|
10047
|
+
const detected = scan.needsSetup.length + scan.alreadyConfigured.length;
|
|
10048
|
+
for (const agent of scan.alreadyConfigured) {
|
|
10049
|
+
printTask("success", agent.name, "already configured", useColors);
|
|
10050
|
+
}
|
|
10051
|
+
for (const agent of scan.needsSetup) {
|
|
10052
|
+
printTask("warning", agent.name, "needs setup", useColors);
|
|
10053
|
+
}
|
|
10054
|
+
if (scan.notDetected.length > 0) {
|
|
10055
|
+
printTask("skipped", `${scan.notDetected.length} supported tool${scan.notDetected.length !== 1 ? "s" : ""} not found`, formatAgentNames(scan.notDetected), useColors);
|
|
10056
|
+
}
|
|
10057
|
+
if (detected > 0) {
|
|
10058
|
+
console.log();
|
|
10059
|
+
console.log(` Found ${detected} supported tool${detected !== 1 ? "s" : ""}.`);
|
|
10060
|
+
}
|
|
10061
|
+
}
|
|
10062
|
+
function printAuthExplanation() {
|
|
10063
|
+
console.log(" GitHits authentication is required before your agent can use GitHits tools.");
|
|
10064
|
+
console.log();
|
|
10065
|
+
console.log(" We'll open your browser to connect your account.");
|
|
10066
|
+
console.log(" Credentials are stored securely in your OS keychain.");
|
|
9099
10067
|
console.log();
|
|
9100
|
-
console.log("
|
|
9101
|
-
console.log(' npx githits@latest example "How do I use useEffect cleanup?"');
|
|
10068
|
+
console.log(" No API keys or secrets are written into your MCP config.");
|
|
9102
10069
|
console.log();
|
|
9103
|
-
|
|
9104
|
-
|
|
10070
|
+
}
|
|
10071
|
+
async function runInitAuthentication(options, promptService, createLoginDeps, useColors) {
|
|
10072
|
+
if (options.skipLogin) {
|
|
10073
|
+
console.log(` Skipping authentication (--skip-login).
|
|
10074
|
+
`);
|
|
10075
|
+
return "skipped";
|
|
10076
|
+
}
|
|
10077
|
+
if (!createLoginDeps) {
|
|
10078
|
+
printTask("warning", "Sign-in unavailable", "sign in later with `githits login`", useColors);
|
|
10079
|
+
return "unavailable";
|
|
10080
|
+
}
|
|
10081
|
+
while (true) {
|
|
10082
|
+
let loginResult;
|
|
10083
|
+
try {
|
|
10084
|
+
const loginDeps = await createLoginDeps();
|
|
10085
|
+
if (loginDeps.hasValidToken) {
|
|
10086
|
+
printTask("success", "Already signed in", undefined, useColors);
|
|
10087
|
+
return "authenticated";
|
|
10088
|
+
}
|
|
10089
|
+
printAuthExplanation();
|
|
10090
|
+
if (!options.yes) {
|
|
10091
|
+
let authChoice;
|
|
10092
|
+
try {
|
|
10093
|
+
authChoice = await promptService.select(" Continue with browser sign-in?", AUTH_START_CHOICES, "sign_in");
|
|
10094
|
+
} catch (err) {
|
|
10095
|
+
if (err instanceof ExitPromptError) {
|
|
10096
|
+
console.log(`
|
|
10097
|
+
Setup cancelled.
|
|
10098
|
+
`);
|
|
10099
|
+
return "cancelled";
|
|
10100
|
+
}
|
|
10101
|
+
throw err;
|
|
10102
|
+
}
|
|
10103
|
+
if (authChoice === "skip") {
|
|
10104
|
+
printTask("warning", "Sign-in skipped", "your agent will ask you to sign in later", useColors);
|
|
10105
|
+
return "skipped";
|
|
10106
|
+
}
|
|
10107
|
+
if (authChoice === "cancel") {
|
|
10108
|
+
console.log("\n Setup cancelled. Run `githits login` to authenticate.\n");
|
|
10109
|
+
return "cancelled";
|
|
10110
|
+
}
|
|
10111
|
+
}
|
|
10112
|
+
loginResult = await loginFlow({}, loginDeps, createInitLoginOutput());
|
|
10113
|
+
} catch (error2) {
|
|
10114
|
+
const msg = error2 instanceof Error ? error2.message : String(error2);
|
|
10115
|
+
loginResult = { status: "failed", message: msg };
|
|
10116
|
+
}
|
|
10117
|
+
if (loginResult.status === "already_authenticated") {
|
|
10118
|
+
printTask("success", "Already signed in", undefined, useColors);
|
|
10119
|
+
return "authenticated";
|
|
10120
|
+
}
|
|
10121
|
+
if (loginResult.status === "success") {
|
|
10122
|
+
printTask("success", "Signed in successfully", undefined, useColors);
|
|
10123
|
+
return "authenticated";
|
|
10124
|
+
}
|
|
10125
|
+
console.log(` ${warning(`Login failed: ${loginResult.message}`, useColors)}
|
|
10126
|
+
`);
|
|
10127
|
+
printAuthRecoveryHint(useColors);
|
|
10128
|
+
if (options.yes) {
|
|
10129
|
+
console.log(` Continuing without authentication...
|
|
10130
|
+
`);
|
|
10131
|
+
return "failed_continue";
|
|
10132
|
+
}
|
|
10133
|
+
let choice;
|
|
10134
|
+
try {
|
|
10135
|
+
choice = await promptService.select(" Authentication failed. What would you like to do?", AUTH_RECOVERY_CHOICES, "retry");
|
|
10136
|
+
} catch (err) {
|
|
10137
|
+
if (err instanceof ExitPromptError) {
|
|
10138
|
+
console.log(`
|
|
10139
|
+
Setup cancelled.
|
|
10140
|
+
`);
|
|
10141
|
+
return "cancelled";
|
|
10142
|
+
}
|
|
10143
|
+
throw err;
|
|
10144
|
+
}
|
|
10145
|
+
if (choice === "retry") {
|
|
10146
|
+
console.log();
|
|
10147
|
+
continue;
|
|
10148
|
+
}
|
|
10149
|
+
if (choice === "cancel") {
|
|
10150
|
+
console.log("\n Setup cancelled. Run `githits login` to authenticate.\n");
|
|
10151
|
+
return "cancelled";
|
|
10152
|
+
}
|
|
10153
|
+
console.log(` Continuing without authentication...
|
|
10154
|
+
`);
|
|
10155
|
+
return "failed_continue";
|
|
10156
|
+
}
|
|
10157
|
+
}
|
|
10158
|
+
function shouldPrintReady(authStatus) {
|
|
10159
|
+
return authStatus === "authenticated";
|
|
10160
|
+
}
|
|
10161
|
+
function printPostSetupNextSteps(authStatus, useColors) {
|
|
10162
|
+
printSection(5, shouldPrintReady(authStatus) ? "Ready" : "Next Steps", useColors);
|
|
10163
|
+
if (shouldPrintReady(authStatus)) {
|
|
10164
|
+
printReadyNextSteps();
|
|
10165
|
+
} else if (authStatus === "failed_continue") {
|
|
10166
|
+
printAuthRequiredNextSteps(useColors);
|
|
10167
|
+
} else {
|
|
10168
|
+
printAuthNotCheckedNextSteps(useColors);
|
|
10169
|
+
}
|
|
9105
10170
|
}
|
|
9106
10171
|
async function verifyAgentConfigured(agent, fileSystemService, execService) {
|
|
9107
10172
|
const postCheck = await scanAgents([agent], fileSystemService, execService);
|
|
@@ -9135,28 +10200,54 @@ async function verifyAgentUnconfigured(agent, fileSystemService, execService) {
|
|
|
9135
10200
|
message: `${agent.name} verification failed: still configured after uninstall.`
|
|
9136
10201
|
};
|
|
9137
10202
|
}
|
|
9138
|
-
async function
|
|
9139
|
-
|
|
9140
|
-
|
|
10203
|
+
async function inspectSetupForUninstall(agent, config, fileSystemService, execService) {
|
|
10204
|
+
if (config.method === "config-file") {
|
|
10205
|
+
const check = await getConfigUninstallCheckStatus(config, fileSystemService);
|
|
10206
|
+
if (check.status === "configured")
|
|
10207
|
+
return "configured";
|
|
10208
|
+
if (check.status === "not_configured")
|
|
10209
|
+
return "not_configured";
|
|
10210
|
+
return { status: "failed", message: check.message };
|
|
10211
|
+
}
|
|
10212
|
+
if (config.method === "cli") {
|
|
10213
|
+
if (!config.checkCommand) {
|
|
10214
|
+
return {
|
|
10215
|
+
status: "failed",
|
|
10216
|
+
message: `${agent.name} does not have a verified uninstall check command.`
|
|
10217
|
+
};
|
|
10218
|
+
}
|
|
10219
|
+
const checkStatus = await getCliCheckStatus(config.checkCommand, execService);
|
|
10220
|
+
if (checkStatus === "configured")
|
|
10221
|
+
return "configured";
|
|
10222
|
+
if (checkStatus === "not_configured")
|
|
10223
|
+
return "not_configured";
|
|
10224
|
+
if (agent.id === "gemini-cli" && await isGeminiExtensionInstalledFromFilesystem(fileSystemService)) {
|
|
10225
|
+
return "configured";
|
|
10226
|
+
}
|
|
9141
10227
|
return {
|
|
9142
10228
|
status: "failed",
|
|
9143
|
-
message:
|
|
10229
|
+
message: `Cannot inspect ${agent.name}: ${config.checkCommand.command} ${config.checkCommand.args.join(" ")} failed.`
|
|
9144
10230
|
};
|
|
9145
10231
|
}
|
|
9146
|
-
const
|
|
9147
|
-
|
|
9148
|
-
|
|
9149
|
-
}
|
|
9150
|
-
|
|
9151
|
-
|
|
10232
|
+
const accumulated = {
|
|
10233
|
+
configured: false,
|
|
10234
|
+
notConfigured: false
|
|
10235
|
+
};
|
|
10236
|
+
for (const step of config.steps) {
|
|
10237
|
+
const check = await inspectSetupForUninstall(agent, step, fileSystemService, execService);
|
|
10238
|
+
if (check === "configured") {
|
|
10239
|
+
accumulated.configured = true;
|
|
10240
|
+
} else if (check === "not_configured") {
|
|
10241
|
+
accumulated.notConfigured = true;
|
|
10242
|
+
} else {
|
|
10243
|
+
accumulated.failure = check;
|
|
10244
|
+
}
|
|
9152
10245
|
}
|
|
9153
|
-
if (
|
|
10246
|
+
if (accumulated.failure)
|
|
10247
|
+
return accumulated.failure;
|
|
10248
|
+
if (accumulated.configured)
|
|
9154
10249
|
return "configured";
|
|
9155
|
-
|
|
9156
|
-
return {
|
|
9157
|
-
status: "failed",
|
|
9158
|
-
message: `Cannot inspect ${agent.name}: ${config.checkCommand.command} ${config.checkCommand.args.join(" ")} failed.`
|
|
9159
|
-
};
|
|
10250
|
+
return "not_configured";
|
|
9160
10251
|
}
|
|
9161
10252
|
async function scanAgentsForUninstall(fileSystemService, execService) {
|
|
9162
10253
|
const setupScan = await scanAgents(agentDefinitions, fileSystemService, execService);
|
|
@@ -9170,35 +10261,41 @@ async function scanAgentsForUninstall(fileSystemService, execService) {
|
|
|
9170
10261
|
...setupScan.alreadyConfigured,
|
|
9171
10262
|
...setupScan.needsSetup
|
|
9172
10263
|
]) {
|
|
9173
|
-
const config = agent
|
|
9174
|
-
|
|
9175
|
-
|
|
9176
|
-
|
|
9177
|
-
|
|
9178
|
-
|
|
9179
|
-
result.failed.push({
|
|
9180
|
-
id: agent.id,
|
|
9181
|
-
name: agent.name,
|
|
9182
|
-
status: "failed",
|
|
9183
|
-
message: check.message
|
|
9184
|
-
});
|
|
9185
|
-
} else {
|
|
9186
|
-
result.notConfigured.push(agent);
|
|
9187
|
-
}
|
|
10264
|
+
const config = getResolvedSetupConfig(agent, fileSystemService);
|
|
10265
|
+
const check = await inspectSetupForUninstall(agent, config, fileSystemService, execService);
|
|
10266
|
+
if (check === "configured") {
|
|
10267
|
+
result.configured.push(agent);
|
|
10268
|
+
} else if (check === "not_configured") {
|
|
10269
|
+
result.notConfigured.push(agent);
|
|
9188
10270
|
} else {
|
|
9189
|
-
|
|
9190
|
-
|
|
9191
|
-
|
|
9192
|
-
|
|
9193
|
-
|
|
9194
|
-
}
|
|
9195
|
-
|
|
9196
|
-
|
|
9197
|
-
|
|
9198
|
-
|
|
9199
|
-
|
|
9200
|
-
|
|
9201
|
-
|
|
10271
|
+
result.failed.push({
|
|
10272
|
+
id: agent.id,
|
|
10273
|
+
name: agent.name,
|
|
10274
|
+
status: "failed",
|
|
10275
|
+
message: check.message
|
|
10276
|
+
});
|
|
10277
|
+
}
|
|
10278
|
+
}
|
|
10279
|
+
for (const agent of setupScan.notDetected) {
|
|
10280
|
+
const piConfigUninstall = getPiConfigFileUninstall(agent, fileSystemService);
|
|
10281
|
+
if (!piConfigUninstall) {
|
|
10282
|
+
continue;
|
|
10283
|
+
}
|
|
10284
|
+
const check = await getConfigUninstallCheckStatus(piConfigUninstall, fileSystemService);
|
|
10285
|
+
if (check.status === "configured") {
|
|
10286
|
+
result.configured.push({
|
|
10287
|
+
...agent,
|
|
10288
|
+
resolvedUninstallConfig: piConfigUninstall,
|
|
10289
|
+
skipUninstallVerification: true
|
|
10290
|
+
});
|
|
10291
|
+
result.notDetected = result.notDetected.filter((a) => a.id !== agent.id);
|
|
10292
|
+
} else if (check.status === "failed") {
|
|
10293
|
+
result.failed.push({
|
|
10294
|
+
id: agent.id,
|
|
10295
|
+
name: agent.name,
|
|
10296
|
+
status: "failed",
|
|
10297
|
+
message: check.message
|
|
10298
|
+
});
|
|
9202
10299
|
}
|
|
9203
10300
|
}
|
|
9204
10301
|
return result;
|
|
@@ -9206,126 +10303,100 @@ async function scanAgentsForUninstall(fileSystemService, execService) {
|
|
|
9206
10303
|
async function initAction(options, deps) {
|
|
9207
10304
|
const useColors = shouldUseColors();
|
|
9208
10305
|
const { fileSystemService, promptService, execService, createLoginDeps } = deps;
|
|
9209
|
-
|
|
9210
|
-
|
|
9211
|
-
|
|
9212
|
-
`);
|
|
9213
|
-
if (!options.skipLogin && createLoginDeps) {
|
|
9214
|
-
console.log(` Checking authentication...
|
|
9215
|
-
`);
|
|
9216
|
-
let loginResult;
|
|
10306
|
+
printInitIntro(useColors);
|
|
10307
|
+
if (!options.yes) {
|
|
10308
|
+
let intent;
|
|
9217
10309
|
try {
|
|
9218
|
-
|
|
9219
|
-
|
|
9220
|
-
|
|
9221
|
-
|
|
9222
|
-
|
|
9223
|
-
}
|
|
9224
|
-
if (loginResult.status === "already_authenticated") {
|
|
9225
|
-
console.log(` ${success("Already authenticated", useColors)}
|
|
9226
|
-
`);
|
|
9227
|
-
} else if (loginResult.status === "success") {
|
|
9228
|
-
console.log(` ${success("Logged in successfully", useColors)}
|
|
9229
|
-
`);
|
|
9230
|
-
} else {
|
|
9231
|
-
console.log(` ${warning(`Login failed: ${loginResult.message}`, useColors)}
|
|
9232
|
-
`);
|
|
9233
|
-
printAuthRecoveryHint();
|
|
9234
|
-
if (!options.yes) {
|
|
9235
|
-
try {
|
|
9236
|
-
const choice = await promptService.confirm3("Continue without authentication?");
|
|
9237
|
-
if (choice === "no") {
|
|
9238
|
-
console.log("\n Setup cancelled. Run `githits login` to authenticate.\n");
|
|
9239
|
-
return;
|
|
9240
|
-
}
|
|
9241
|
-
} catch (err) {
|
|
9242
|
-
if (err instanceof ExitPromptError) {
|
|
9243
|
-
console.log(`
|
|
9244
|
-
Setup cancelled.
|
|
10310
|
+
intent = await promptService.select(" What do you want to do?", INIT_INTENT_CHOICES, "mcp");
|
|
10311
|
+
} catch (err) {
|
|
10312
|
+
if (err instanceof ExitPromptError) {
|
|
10313
|
+
console.log(`
|
|
10314
|
+
Setup cancelled. No changes made.
|
|
9245
10315
|
`);
|
|
9246
|
-
|
|
9247
|
-
}
|
|
9248
|
-
throw err;
|
|
9249
|
-
}
|
|
10316
|
+
return;
|
|
9250
10317
|
}
|
|
9251
|
-
|
|
9252
|
-
|
|
9253
|
-
|
|
10318
|
+
throw err;
|
|
10319
|
+
}
|
|
10320
|
+
if (intent === "skills") {
|
|
10321
|
+
printSkillsInstructions(useColors);
|
|
10322
|
+
return;
|
|
10323
|
+
}
|
|
10324
|
+
if (intent === "later") {
|
|
10325
|
+
console.log("\n No changes made. Run `githits init` whenever you're ready.\n");
|
|
10326
|
+
return;
|
|
9254
10327
|
}
|
|
9255
10328
|
}
|
|
9256
|
-
|
|
9257
|
-
|
|
9258
|
-
const
|
|
9259
|
-
|
|
9260
|
-
|
|
9261
|
-
|
|
9262
|
-
|
|
9263
|
-
|
|
9264
|
-
|
|
9265
|
-
for (const agent of scan.notDetected) {
|
|
9266
|
-
console.log(` ${colorize(`${agent.name} — not detected`, "dim", useColors)}`);
|
|
10329
|
+
printSection(1, "Detect tools", useColors);
|
|
10330
|
+
console.log(" Scanning for compatible AI coding tools...");
|
|
10331
|
+
const progress = createScanProgressReporter(useColors);
|
|
10332
|
+
const scanPromise = startSafeInitScan(fileSystemService, execService, (scanProgress) => progress.onProgress(scanProgress));
|
|
10333
|
+
let scan;
|
|
10334
|
+
try {
|
|
10335
|
+
scan = await unwrapSafeScan(scanPromise);
|
|
10336
|
+
} finally {
|
|
10337
|
+
progress.finish();
|
|
9267
10338
|
}
|
|
9268
|
-
|
|
10339
|
+
printScanSummary(scan, useColors);
|
|
9269
10340
|
if (scan.needsSetup.length === 0 && scan.alreadyConfigured.length === 0) {
|
|
9270
|
-
|
|
9271
|
-
|
|
10341
|
+
printTask("warning", "No supported AI coding tools detected", "install a supported tool and run `githits init` again", useColors);
|
|
10342
|
+
console.log();
|
|
9272
10343
|
return;
|
|
9273
10344
|
}
|
|
9274
|
-
|
|
9275
|
-
|
|
9276
|
-
|
|
9277
|
-
|
|
9278
|
-
|
|
10345
|
+
let toSetup = scan.needsSetup;
|
|
10346
|
+
printSection(2, "Choose tools", useColors);
|
|
10347
|
+
if (!options.yes && scan.needsSetup.length > 0) {
|
|
10348
|
+
try {
|
|
10349
|
+
toSetup = await promptService.checkbox(" Select which tools should use GitHits:", buildInitAgentChoices(scan));
|
|
10350
|
+
} catch (err) {
|
|
10351
|
+
if (err instanceof ExitPromptError) {
|
|
10352
|
+
console.log(`
|
|
10353
|
+
Setup cancelled. No changes made.
|
|
10354
|
+
`);
|
|
10355
|
+
return;
|
|
10356
|
+
}
|
|
10357
|
+
throw err;
|
|
9279
10358
|
}
|
|
9280
|
-
|
|
9281
|
-
|
|
10359
|
+
} else if (scan.needsSetup.length === 0) {
|
|
10360
|
+
printTask("success", "No tool changes needed", "all detected tools already have GitHits MCP", useColors);
|
|
10361
|
+
} else {
|
|
10362
|
+
printTask("success", "Selected all detected tools", "--yes", useColors);
|
|
10363
|
+
}
|
|
10364
|
+
const outcomes = [];
|
|
10365
|
+
if (toSetup.length === 0 && scan.needsSetup.length > 0) {
|
|
10366
|
+
printTask("skipped", "Setup skipped", "no tools selected", useColors);
|
|
9282
10367
|
console.log();
|
|
9283
10368
|
return;
|
|
9284
10369
|
}
|
|
9285
|
-
|
|
9286
|
-
const
|
|
9287
|
-
|
|
9288
|
-
|
|
9289
|
-
|
|
9290
|
-
|
|
9291
|
-
|
|
9292
|
-
|
|
9293
|
-
|
|
9294
|
-
`)) {
|
|
9295
|
-
console.log(` ${line}`);
|
|
9296
|
-
}
|
|
10370
|
+
printSection(3, "Sign in", useColors);
|
|
10371
|
+
const authStatus = await runInitAuthentication(options, promptService, createLoginDeps, useColors);
|
|
10372
|
+
if (authStatus === "cancelled") {
|
|
10373
|
+
return;
|
|
10374
|
+
}
|
|
10375
|
+
printSection(4, "Install and verify", useColors);
|
|
10376
|
+
if (toSetup.length === 0) {
|
|
10377
|
+
printTask("success", "Nothing to install", "all detected tools are already configured", useColors);
|
|
10378
|
+
printPostSetupNextSteps(authStatus, useColors);
|
|
9297
10379
|
console.log();
|
|
9298
|
-
|
|
9299
|
-
|
|
9300
|
-
|
|
9301
|
-
|
|
9302
|
-
|
|
9303
|
-
|
|
9304
|
-
|
|
9305
|
-
|
|
9306
|
-
|
|
9307
|
-
|
|
10380
|
+
return;
|
|
10381
|
+
}
|
|
10382
|
+
const installTasks = createInstallTaskReporter(useColors);
|
|
10383
|
+
for (const agent of toSetup) {
|
|
10384
|
+
const config = getResolvedSetupConfig(agent, fileSystemService);
|
|
10385
|
+
const finishTask = installTasks.start(agent.name);
|
|
10386
|
+
let result;
|
|
10387
|
+
try {
|
|
10388
|
+
result = config.method === "cli" ? await executeCliSetup(config, execService) : config.method === "config-file" ? await executeConfigFileSetup(config, fileSystemService) : await executeCompositeSetup(config, fileSystemService, execService);
|
|
10389
|
+
if (result.status === "success" || result.status === "already_configured") {
|
|
10390
|
+
const verification = await verifyAgentConfigured(agent, fileSystemService, execService);
|
|
10391
|
+
if (!verification.ok) {
|
|
10392
|
+
result = {
|
|
10393
|
+
status: "failed",
|
|
10394
|
+
message: agent.id === "gemini-cli" ? "Gemini installation did not complete. Retry, or run: gemini extensions install --consent https://github.com/githits-com/githits-cli" : verification.message ?? `${agent.name} verification failed after setup.`
|
|
10395
|
+
};
|
|
9308
10396
|
}
|
|
9309
|
-
throw err;
|
|
9310
|
-
}
|
|
9311
|
-
if (choice === "no") {
|
|
9312
|
-
outcomes.push({ id: agent.id, name: agent.name, status: "skipped" });
|
|
9313
|
-
console.log();
|
|
9314
|
-
continue;
|
|
9315
|
-
}
|
|
9316
|
-
if (choice === "always") {
|
|
9317
|
-
alwaysMode = true;
|
|
9318
|
-
}
|
|
9319
|
-
}
|
|
9320
|
-
let result = config.method === "cli" ? await executeCliSetup(config, execService) : await executeConfigFileSetup(config, fileSystemService);
|
|
9321
|
-
if (result.status === "success" || result.status === "already_configured") {
|
|
9322
|
-
const verification = await verifyAgentConfigured(agent, fileSystemService, execService);
|
|
9323
|
-
if (!verification.ok) {
|
|
9324
|
-
result = {
|
|
9325
|
-
status: "failed",
|
|
9326
|
-
message: agent.id === "gemini-cli" ? "Gemini installation did not complete. Retry, or run: gemini extensions install --consent https://github.com/githits-com/githits-cli" : verification.message ?? `${agent.name} verification failed after setup.`
|
|
9327
|
-
};
|
|
9328
10397
|
}
|
|
10398
|
+
} finally {
|
|
10399
|
+
finishTask();
|
|
9329
10400
|
}
|
|
9330
10401
|
outcomes.push({
|
|
9331
10402
|
id: agent.id,
|
|
@@ -9334,38 +10405,30 @@ async function initAction(options, deps) {
|
|
|
9334
10405
|
message: result.status === "failed" ? result.message : undefined
|
|
9335
10406
|
});
|
|
9336
10407
|
if (result.status === "success") {
|
|
9337
|
-
|
|
9338
|
-
`);
|
|
10408
|
+
printTask("success", agent.name, "configured and verified", useColors);
|
|
9339
10409
|
} else if (result.status === "already_configured") {
|
|
9340
|
-
|
|
9341
|
-
`);
|
|
10410
|
+
printTask("warning", agent.name, "already configured", useColors);
|
|
9342
10411
|
} else {
|
|
9343
|
-
|
|
9344
|
-
`);
|
|
10412
|
+
printTask("failed", agent.name, result.message, useColors);
|
|
9345
10413
|
}
|
|
9346
10414
|
}
|
|
10415
|
+
console.log();
|
|
9347
10416
|
const configured = outcomes.filter((o) => o.status === "success").length;
|
|
9348
10417
|
const alreadyDone = outcomes.filter((o) => o.status === "already_configured").length + scan.alreadyConfigured.length;
|
|
9349
10418
|
const failed = outcomes.filter((o) => o.status === "failed").length;
|
|
9350
|
-
const skipped = outcomes.filter((o) => o.status === "skipped").length;
|
|
9351
10419
|
if (failed > 0) {
|
|
9352
10420
|
console.log(" Setup completed with errors.");
|
|
9353
|
-
} else if (continuedWithoutAuth && (configured > 0 || alreadyDone > 0)) {
|
|
9354
|
-
console.log(" MCP is configured, but authentication is still required.");
|
|
9355
|
-
console.log(" Run `githits login` before using GitHits tools.");
|
|
9356
10421
|
} else if (configured > 0 || alreadyDone > 0) {
|
|
9357
|
-
|
|
9358
|
-
} else if (skipped > 0) {
|
|
9359
|
-
console.log(" Setup skipped.");
|
|
10422
|
+
printPostSetupNextSteps(authStatus, useColors);
|
|
9360
10423
|
}
|
|
9361
10424
|
if (failed > 0) {
|
|
9362
|
-
console.log(` ${failed}
|
|
10425
|
+
console.log(` ${failed} tool${failed !== 1 ? "s" : ""} failed to configure.`);
|
|
9363
10426
|
for (const outcome of outcomes.filter((o) => o.status === "failed")) {
|
|
9364
10427
|
console.log(` - ${outcome.name}: ${outcome.message ?? "Unknown error"}`);
|
|
9365
10428
|
}
|
|
9366
10429
|
}
|
|
9367
|
-
if (
|
|
9368
|
-
console.log(` ${
|
|
10430
|
+
if (scan.alreadyConfigured.length > 0) {
|
|
10431
|
+
console.log(` ${scan.alreadyConfigured.length} tool${scan.alreadyConfigured.length !== 1 ? "s" : ""} already configured.`);
|
|
9369
10432
|
}
|
|
9370
10433
|
console.log();
|
|
9371
10434
|
}
|
|
@@ -9373,7 +10436,8 @@ async function initUninstallAction(options, deps) {
|
|
|
9373
10436
|
const useColors = shouldUseColors();
|
|
9374
10437
|
const { fileSystemService, promptService, execService } = deps;
|
|
9375
10438
|
console.log(`
|
|
9376
|
-
${colorize("GitHits", "bold", useColors)}
|
|
10439
|
+
${colorize("Disconnect GitHits from your coding agents.", "bold", useColors)}`);
|
|
10440
|
+
console.log(` ${colorize("Removes the local GitHits MCP configuration.", "dim", useColors)}
|
|
9377
10441
|
`);
|
|
9378
10442
|
console.log(` Scanning for configured agents...
|
|
9379
10443
|
`);
|
|
@@ -9401,8 +10465,8 @@ async function initUninstallAction(options, deps) {
|
|
|
9401
10465
|
for (const agent of scan.configured) {
|
|
9402
10466
|
console.log(` Uninstalling from ${colorize(agent.name, "bold", useColors)}...
|
|
9403
10467
|
`);
|
|
9404
|
-
const setupConfig = agent
|
|
9405
|
-
const uninstallConfig = setupConfig.method === "config-file" ? setupConfig : agent.getUninstallConfig?.(fileSystemService);
|
|
10468
|
+
const setupConfig = getResolvedSetupConfig(agent, fileSystemService);
|
|
10469
|
+
const uninstallConfig = agent.resolvedUninstallConfig ?? (setupConfig.method === "config-file" ? setupConfig : agent.getUninstallConfig?.(fileSystemService, agent.resolvedSetupContext));
|
|
9406
10470
|
if (!uninstallConfig) {
|
|
9407
10471
|
outcomes.push({
|
|
9408
10472
|
id: agent.id,
|
|
@@ -9442,8 +10506,8 @@ async function initUninstallAction(options, deps) {
|
|
|
9442
10506
|
alwaysMode = true;
|
|
9443
10507
|
}
|
|
9444
10508
|
}
|
|
9445
|
-
let result = uninstallConfig.method === "cli" ? await executeCliUninstall(uninstallConfig, execService) : await executeConfigFileUninstall(uninstallConfig, fileSystemService);
|
|
9446
|
-
if (result.status === "removed") {
|
|
10509
|
+
let result = uninstallConfig.method === "cli" ? await executeCliUninstall(uninstallConfig, execService) : uninstallConfig.method === "config-file" ? await executeConfigFileUninstall(uninstallConfig, fileSystemService) : await executeCompositeUninstall(uninstallConfig, fileSystemService, execService);
|
|
10510
|
+
if (result.status === "removed" && !agent.skipUninstallVerification) {
|
|
9447
10511
|
const verification = await verifyAgentUnconfigured(agent, fileSystemService, execService);
|
|
9448
10512
|
if (!verification.ok) {
|
|
9449
10513
|
result = {
|
|
@@ -9510,32 +10574,27 @@ async function initUninstallAction(options, deps) {
|
|
|
9510
10574
|
}
|
|
9511
10575
|
console.log();
|
|
9512
10576
|
}
|
|
9513
|
-
function printAuthRecoveryHint() {
|
|
10577
|
+
function printAuthRecoveryHint(useColors) {
|
|
9514
10578
|
console.log(" You can still configure MCP, but GitHits tools will require auth.");
|
|
9515
10579
|
console.log(" Recovery steps:");
|
|
9516
|
-
console.log("
|
|
9517
|
-
console.log("
|
|
10580
|
+
console.log(` ${formatCommand("githits auth status", useColors)}`);
|
|
10581
|
+
console.log(` ${formatCommand("githits login --force", useColors)}`);
|
|
9518
10582
|
console.log(" For CI or locked-down machines, set GITHITS_API_TOKEN.");
|
|
9519
10583
|
console.log(` If your system keychain is unavailable, set GITHITS_AUTH_STORAGE=file after accepting plaintext storage.
|
|
9520
10584
|
`);
|
|
9521
10585
|
}
|
|
9522
|
-
var INIT_DESCRIPTION = `
|
|
9523
|
-
|
|
9524
|
-
Authenticates with your GitHits account, then scans for available agents
|
|
9525
|
-
(Claude Code, Cursor, Windsurf, VS Code, Cline, Claude Desktop, Codex CLI,
|
|
9526
|
-
Gemini CLI, Google Antigravity), checks which are already configured,
|
|
9527
|
-
and sets up unconfigured ones with your confirmation.
|
|
10586
|
+
var INIT_DESCRIPTION = `Connect GitHits to your coding agents.
|
|
9528
10587
|
|
|
9529
|
-
|
|
9530
|
-
|
|
9531
|
-
|
|
10588
|
+
Installs the local GitHits MCP server — the recommended way to connect — or
|
|
10589
|
+
sets up Agent Skills instead. Detects supported coding tools on this machine,
|
|
10590
|
+
signs you in, and configures the tools you select.`;
|
|
9532
10591
|
var INIT_UNINSTALL_DESCRIPTION = `Remove GitHits MCP server configuration from your coding agents.
|
|
9533
10592
|
|
|
9534
10593
|
Scans for available agents that currently have GitHits configured, then removes
|
|
9535
10594
|
only the GitHits MCP/plugin configuration with your confirmation. Authentication
|
|
9536
10595
|
tokens are not removed; use \`githits logout\` to remove stored credentials.`;
|
|
9537
10596
|
function registerInitCommand(program) {
|
|
9538
|
-
const initCommand = program.command("init").summary("
|
|
10597
|
+
const initCommand = program.command("init").summary("Connect GitHits to your coding agents").description(INIT_DESCRIPTION).option("-y, --yes", "Skip prompts, configure all detected tools").option("--skip-login", "Skip authentication step").action(async (options) => {
|
|
9539
10598
|
const fileSystemService = new FileSystemServiceImpl;
|
|
9540
10599
|
const promptService = new PromptServiceImpl;
|
|
9541
10600
|
const execService = new ExecServiceImpl;
|
|
@@ -9543,7 +10602,7 @@ function registerInitCommand(program) {
|
|
|
9543
10602
|
fileSystemService,
|
|
9544
10603
|
promptService,
|
|
9545
10604
|
execService,
|
|
9546
|
-
createLoginDeps: () =>
|
|
10605
|
+
createLoginDeps: () => createContainer()
|
|
9547
10606
|
});
|
|
9548
10607
|
});
|
|
9549
10608
|
initCommand.command("uninstall").summary("Remove MCP server from your coding agents").description(INIT_UNINSTALL_DESCRIPTION).option("-y, --yes", "Skip prompts, uninstall from all configured agents").action(async (options) => {
|
|
@@ -9779,11 +10838,11 @@ var structuredCodeTargetSchema = z3.object({
|
|
|
9779
10838
|
package_name: z3.string().max(255).optional().describe("Package name. Required for package scope."),
|
|
9780
10839
|
version: z3.string().max(100).optional().describe("Package version, e.g. '4.18.2' (defaults to latest). For package scope only."),
|
|
9781
10840
|
repo_url: z3.string().optional().describe("Repository URL (GitHub). Required for repo scope. Example: https://github.com/expressjs/express"),
|
|
9782
|
-
git_ref: z3.string().optional().describe("Git ref - tag, branch, or
|
|
9783
|
-
}).describe("Target: provide registry + package_name (package scope) or repo_url
|
|
10841
|
+
git_ref: z3.string().optional().describe("Git ref - tag, branch, commit, or HEAD. Omit with repo_url to request the backend-resolved default branch.")
|
|
10842
|
+
}).describe("Target: provide registry + package_name (package scope) or repo_url with optional git_ref (repo scope; omitted ref means default branch intent).");
|
|
9784
10843
|
var codeTargetSchema = z3.union([
|
|
9785
10844
|
structuredCodeTargetSchema,
|
|
9786
|
-
z3.string().min(1).describe("Compact target string. Package: `npm:react@18.2.0` or `npm:react` for latest release. Repository: `https://github.com/facebook/react#HEAD`
|
|
10845
|
+
z3.string().min(1).describe("Compact target string. Package: `npm:react@18.2.0` or `npm:react` for latest release. Repository: `https://github.com/facebook/react#HEAD` or `https://github.com/facebook/react` for default branch intent.")
|
|
9787
10846
|
]);
|
|
9788
10847
|
function resolveCodeTarget(target) {
|
|
9789
10848
|
if (typeof target === "string") {
|
|
@@ -9796,10 +10855,10 @@ function resolveCodeTarget(target) {
|
|
|
9796
10855
|
const hasPackageTarget = Boolean(target.registry || target.package_name);
|
|
9797
10856
|
const hasRepoTarget = Boolean(target.repo_url || target.git_ref);
|
|
9798
10857
|
if (hasPackageTarget && hasRepoTarget) {
|
|
9799
|
-
return invalidTargetResult("Invalid target: provide either registry + package_name or repo_url
|
|
10858
|
+
return invalidTargetResult("Invalid target: provide either registry + package_name or repo_url with optional git_ref, not both.");
|
|
9800
10859
|
}
|
|
9801
10860
|
if (!hasPackageTarget && !hasRepoTarget) {
|
|
9802
|
-
return invalidTargetResult("Missing target: provide registry + package_name or repo_url
|
|
10861
|
+
return invalidTargetResult("Missing target: provide registry + package_name or repo_url.");
|
|
9803
10862
|
}
|
|
9804
10863
|
if (hasPackageTarget) {
|
|
9805
10864
|
if (!target.registry || !target.package_name) {
|
|
@@ -9811,11 +10870,8 @@ function resolveCodeTarget(target) {
|
|
|
9811
10870
|
version: target.version
|
|
9812
10871
|
};
|
|
9813
10872
|
}
|
|
9814
|
-
if (!target.repo_url
|
|
9815
|
-
|
|
9816
|
-
return invalidTargetResult("Incomplete repository target: repo_url is required.");
|
|
9817
|
-
}
|
|
9818
|
-
return invalidTargetResult("Incomplete repository target: git_ref is required for code_files/code_read/code_grep.");
|
|
10873
|
+
if (!target.repo_url) {
|
|
10874
|
+
return invalidTargetResult("Incomplete repository target: repo_url is required.");
|
|
9819
10875
|
}
|
|
9820
10876
|
return {
|
|
9821
10877
|
repoUrl: target.repo_url,
|
|
@@ -9861,7 +10917,7 @@ var schema3 = {
|
|
|
9861
10917
|
wait_timeout_ms: z4.number().optional(),
|
|
9862
10918
|
format: z4.enum(["json", "text", "text-v1"]).optional().describe('Response format. Default `text-v1` — compact line-oriented output (matches grouped by file with grep -A/-B notation for context). Pass `format: "json"` for the structured envelope. `text` is an alias for `text-v1`. Errors stay JSON-formatted in either mode for now.')
|
|
9863
10919
|
};
|
|
9864
|
-
var DESCRIPTION3 = "Deterministic text or regex grep over indexed dependency and repository source files. " + 'Use this when you know the pattern (literal by default; pass `pattern_type: "regex"` for RE2). ' + "Use `search` for discovery instead. " + "Whole-target grep is the default — narrow with `path`, `path_prefix`, `globs`, or `extensions` to keep responses small. " + "Each match's `filePath` (or text file heading) chains into `code_read.path`; pick a window around `match.line` for `code_read.start_line` / `end_line`." + `
|
|
10920
|
+
var DESCRIPTION3 = "Deterministic text or regex grep over indexed dependency and repository source files. " + 'Use this when you know the pattern (literal by default; pass `pattern_type: "regex"` for RE2). ' + "Use `search` for discovery instead. " + "Whole-target grep is the default — narrow with `path`, `path_prefix`, `globs`, or `extensions` to keep responses small. " + "Each match's `filePath` (or text file heading) chains into `code_read.path`; pick a window around `match.line` for `code_read.start_line` / `end_line`. " + "When fresh data is not ready within the wait window, responses may include `targetResolution` provenance and immediately-queryable alternatives in error details." + `
|
|
9865
10921
|
|
|
9866
10922
|
${CODE_GREP_GUARDRAIL}`;
|
|
9867
10923
|
function createGrepRepoTool(service) {
|
|
@@ -9955,10 +11011,10 @@ var schema4 = {
|
|
|
9955
11011
|
exclude_test_files: z5.boolean().optional(),
|
|
9956
11012
|
include_hidden: z5.boolean().optional(),
|
|
9957
11013
|
limit: z5.number().optional().describe("Max entries to return (1–1000, default 200). Out-of-range values return an `INVALID_ARGUMENT` envelope."),
|
|
9958
|
-
wait_timeout_ms: z5.number().optional().describe("Max milliseconds to wait for indexing (0–60000, default 20000). On an `INDEXING` error envelope, retry with a longer timeout or pass a version from `details.availableVersions`."),
|
|
11014
|
+
wait_timeout_ms: z5.number().optional().describe("Max milliseconds to wait for indexing (0–60000, default 20000). On an `INDEXING` error envelope, retry with a longer timeout or pass a version/ref from `details.availableVersions` / `details.availableRefs`."),
|
|
9959
11015
|
format: z5.enum(["json", "text", "text-v1"]).optional().describe('Response format. Default `text-v1` — compact paths-only listing. Pass `format: "json"` for the structured envelope. `text` is an alias for `text-v1`. Errors stay JSON-formatted in either mode for now.')
|
|
9960
11016
|
};
|
|
9961
|
-
var DESCRIPTION4 = "List files in an indexed dependency. First choice for file/path " + "enumeration tasks such as files under a directory; use " + "`path_prefix` for directory prefixes (e.g. `lib/`) and optional " + "`extensions` for language filtering. Use this to discover paths " + "before `code_read` (when `code_read` returns `FILE_NOT_FOUND` or " + "you don't yet know the path) and to scope `code_grep`. Address " + "via `target.registry` + `target.package_name` (package scope) or " + "`target.repo_url` + `target.git_ref` (repo scope), mutually " + "exclusive. Narrow with `path`, `path_prefix`, `globs`, " + "`extensions`, `file_types`, `languages`, or file-intent filters. " + "JSON envelope shape: `{total, hasMore, files: [{path, name, " + "language, fileType, byteSize}], resolution, indexedVersion}`. " + "
|
|
11017
|
+
var DESCRIPTION4 = "List files in an indexed dependency. First choice for file/path " + "enumeration tasks such as files under a directory; use " + "`path_prefix` for directory prefixes (e.g. `lib/`) and optional " + "`extensions` for language filtering. Use this to discover paths " + "before `code_read` (when `code_read` returns `FILE_NOT_FOUND` or " + "you don't yet know the path) and to scope `code_grep`. Address " + "via `target.registry` + `target.package_name` (package scope) or " + "`target.repo_url` + optional `target.git_ref` (repo scope), mutually " + "exclusive. Narrow with `path`, `path_prefix`, `globs`, " + "`extensions`, `file_types`, `languages`, or file-intent filters. " + "JSON envelope shape: `{total, hasMore, files: [{path, name, " + "language, fileType, byteSize}], resolution, indexedVersion}`. " + "When fresh data is not ready within the wait window, responses may " + "include `targetResolution` provenance and immediately-queryable " + "alternatives. On an `INDEXING` error envelope, retry with a longer " + "`wait_timeout_ms` or use a version/ref from `details.availableVersions` " + "/ `details.availableRefs`.";
|
|
9962
11018
|
function createListFilesTool(service) {
|
|
9963
11019
|
return {
|
|
9964
11020
|
name: "code_files",
|
|
@@ -10721,10 +11777,10 @@ var schema11 = {
|
|
|
10721
11777
|
path: z12.string().describe("Exact file path to read, not a directory. Package addressing: package-relative. Repo addressing: repo-relative. Use `code_files` with `path_prefix` to list directories, then pass an emitted `path` here."),
|
|
10722
11778
|
start_line: z12.number().optional().describe(`Starting line (1-indexed). Omit to start at line 1. The MCP surface caps any single read at ${MCP_READ_MAX_SPAN} lines — pick a focused window from your prior \`search\` / \`code_grep\` hit.`),
|
|
10723
11779
|
end_line: z12.number().optional().describe(`Ending line (inclusive). Must be ≥ \`start_line\` when both are set. Omitting it implies \`start_line + ${MCP_READ_MAX_SPAN - 1}\` because the MCP surface caps each read at ${MCP_READ_MAX_SPAN} lines.`),
|
|
10724
|
-
wait_timeout_ms: z12.number().optional().describe("Max milliseconds to wait for indexing (0–60000, default 20000). On an `INDEXING` error envelope, retry with a longer timeout or pass a version from `details.availableVersions`."),
|
|
11780
|
+
wait_timeout_ms: z12.number().optional().describe("Max milliseconds to wait for indexing (0–60000, default 20000). On an `INDEXING` error envelope, retry with a longer timeout or pass a version/ref from `details.availableVersions` / `details.availableRefs`."),
|
|
10725
11781
|
format: z12.enum(["json", "text", "text-v1"]).optional().describe('Response format. Default `text-v1` — line-numbered source content. Pass `format: "json"` for the structured envelope.')
|
|
10726
11782
|
};
|
|
10727
|
-
var DESCRIPTION11 = "Read one exact file from an indexed dependency; it does not list " + "directories. Use `code_files` with `path_prefix` for file/path " + "enumeration. **MCP cap: " + `${MCP_READ_MAX_SPAN} lines per call** — broader requests (or no ` + `range) silently truncate to the first ${MCP_READ_MAX_SPAN} lines ` + "from your start, with a `hint` describing what was returned vs. " + "requested. Pick a focused window from a `search` / `code_grep` " + "match. Response: `{path, language, totalLines, startLine, endLine, " + "content, isBinary, hint?}`. Binary files set `isBinary: true` and " + "omit `content`. Pass the same `path` emitted by `code_files`. " + "Address via `target.registry` + `target.package_name` (package " + "scope) or `target.repo_url` + `target.git_ref` (repo scope), " + "mutually exclusive. On `INDEXING` retry with a
|
|
11783
|
+
var DESCRIPTION11 = "Read one exact file from an indexed dependency; it does not list " + "directories. Use `code_files` with `path_prefix` for file/path " + "enumeration. **MCP cap: " + `${MCP_READ_MAX_SPAN} lines per call** — broader requests (or no ` + `range) silently truncate to the first ${MCP_READ_MAX_SPAN} lines ` + "from your start, with a `hint` describing what was returned vs. " + "requested. Pick a focused window from a `search` / `code_grep` " + "match. Response: `{path, language, totalLines, startLine, endLine, " + "content, isBinary, hint?}`. Binary files set `isBinary: true` and " + "omit `content`. Pass the same `path` emitted by `code_files`. " + "Address via `target.registry` + `target.package_name` (package " + "scope) or `target.repo_url` + optional `target.git_ref` (repo scope), " + "mutually exclusive. When fresh data is not ready within the wait " + "window, responses may include `targetResolution` provenance and " + "immediately-queryable alternatives. On `INDEXING` retry with a " + "longer `wait_timeout_ms` or use a version/ref from error details. " + "On `NOT_FOUND` / `FILE_NOT_FOUND` call " + "`code_files` to discover the actual path." + `
|
|
10728
11784
|
|
|
10729
11785
|
${CODE_READ_GUARDRAIL}`;
|
|
10730
11786
|
function deriveBoundedRange(startLine, endLine) {
|
|
@@ -11124,7 +12180,7 @@ var CORE_BLOCK = `GitHits provides verified, canonical code examples from global
|
|
|
11124
12180
|
\`get_example\` workflow: pass \`language\` only when you know the exact name; otherwise call \`search_language\` first. Default output is markdown with a trailing \`solution_id\`. Reuse prior results before searching again. For dependency-specific grounding, prefer package-scoped \`search\` before global \`get_example\`.`;
|
|
11125
12181
|
var PACKAGE_TOOLS_PREAMBLE = `Indexed package/source tools inspect third-party dependency source, docs, and registry metadata. Use them when a stack trace points into a dependency, you need to verify how a library works, or you're evaluating whether to add or upgrade a package.
|
|
11126
12182
|
|
|
11127
|
-
Targets: \`registry:name[@version]\` (\`registry:name\` = latest release).
|
|
12183
|
+
Targets: \`registry:name[@version]\` (\`registry:name\` = latest release). Repo targets can request the backend default-branch intent by omitting the ref (\`https://github.com/org/repo\` for \`search\`, or \`repo_url\` without \`git_ref\` for \`code_*\`); pass \`#HEAD\` / \`git_ref:"HEAD"\` when you specifically need latest HEAD. Default outputs are compact \`text-v1\`; pass \`format: "json"\` only for structured parsing.`;
|
|
11128
12184
|
var MULTI_TURN_TIP = '**Delegate multi-call work to a sub-agent.** Code-navigation work (`search`, `code_grep`, `code_read`, `code_files`) often takes 3-10 calls. For cross-project comparisons, codebase mapping, pattern surveys, or "how does X actually work" investigations, spawn a sub-task/sub-agent and ask for a compact synthesis. Do it inline only when the raw snippet belongs in the main conversation.';
|
|
11129
12185
|
var SEARCH_BULLET = '- `search` — unified search across indexed dependency code, docs, and symbols. Omit `sources` for AUTO. Use `sources:["code"]` for implementation/examples/tests text, `sources:["symbol"]` for precise API/entity lookup, and `sources:["docs"]` for guides/reference/changelogs. Default output is compact `text-v1` with ready-to-call follow-up arguments; pass `format: "json"` for structured locators, highlights, and source status. Complete by default; opt into partial hits with `allow_partial_results: true`. Incomplete responses carry a `searchRef`.';
|
|
11130
12186
|
var SEARCH_STATUS_BULLET = "- `search_status` — follow up a prior `search` by `searchRef` to check progress, fetch partial hits, or fetch final results.";
|
|
@@ -11251,7 +12307,7 @@ function showMcpSetupInstructions() {
|
|
|
11251
12307
|
console.log("Learn more at https://githits.com");
|
|
11252
12308
|
}
|
|
11253
12309
|
function registerMcpCommand(program) {
|
|
11254
|
-
const mcpCommand = program.command("mcp").summary("Show setup instructions or start MCP server").description(`Start the Model Context Protocol (MCP) server using STDIO transport.
|
|
12310
|
+
const mcpCommand = program.command("mcp").summary("Show MCP setup instructions or start the local MCP server").description(`Start the Model Context Protocol (MCP) server using STDIO transport.
|
|
11255
12311
|
|
|
11256
12312
|
When run interactively (TTY), shows setup instructions.
|
|
11257
12313
|
When run via stdio (non-TTY), starts the MCP server.
|
|
@@ -11823,7 +12879,7 @@ async function registerPkgCommandGroup(program, options = {}) {
|
|
|
11823
12879
|
if (!registration.shouldRegister) {
|
|
11824
12880
|
return;
|
|
11825
12881
|
}
|
|
11826
|
-
const pkgCommand = program.command("pkg").summary("Package metadata
|
|
12882
|
+
const pkgCommand = program.command("pkg").summary("Package metadata, dependencies, vulnerabilities and changelogs").description("Inspect package metadata from npm, PyPI, Hex, Crates, NuGet, Maven, Packagist, RubyGems, Go, vcpkg, and Zig: overviews, advisories, dependency graphs, and changelogs. Advisory data is unavailable for vcpkg and Zig. For source-level operations inside a dependency, use `githits code`.");
|
|
11827
12883
|
registerPkgInfoCommand(pkgCommand);
|
|
11828
12884
|
registerPkgVulnsCommand(pkgCommand);
|
|
11829
12885
|
registerPkgDepsCommand(pkgCommand);
|
|
@@ -11852,7 +12908,8 @@ async function searchAction(query, options, deps) {
|
|
|
11852
12908
|
offset: parseOptionalInt(options.offset, "--offset", 0),
|
|
11853
12909
|
waitTimeoutMs: parseWaitMs(options.wait)
|
|
11854
12910
|
});
|
|
11855
|
-
const
|
|
12911
|
+
const spinner = startSpinner(SPINNER_MESSAGES.search, !options.json);
|
|
12912
|
+
const outcome = await service.search(built.params).finally(() => spinner.stop());
|
|
11856
12913
|
const payload = buildUnifiedSearchSuccessPayload(built.params, built.rawQuery, built.compiledQuery, outcome);
|
|
11857
12914
|
if (options.json) {
|
|
11858
12915
|
console.log(JSON.stringify(payload));
|
|
@@ -11914,7 +12971,7 @@ Pass the searchRef returned by githits search when the initial request could
|
|
|
11914
12971
|
not complete within the wait window. This can return progress, partial hits when
|
|
11915
12972
|
the original request used --allow-partial, or final results.`;
|
|
11916
12973
|
function registerSearchCommand(program) {
|
|
11917
|
-
program.command("search").summary("
|
|
12974
|
+
program.command("search").summary("Explore repository code, dependencies, docs and symbols").description(SEARCH_DESCRIPTION).argument("<query>", "Search query").requiredOption("--in <target>", "Search target: registry:name[@version] or https://github.com/org/repo[#ref]", collectRepeatable3, []).addOption(new Option3("--source <source>", "Source to search (repeatable; default: auto)").choices(["docs", "code", "symbol"]).argParser((value, previous = []) => collectRepeatable3(value.toLowerCase(), previous)).default(undefined)).addOption(new Option3("--kind <kind>", "Precise symbol kind filter").choices([
|
|
11918
12975
|
...knownSymbolKindList()
|
|
11919
12976
|
])).addOption(new Option3("--category <category>", "Broad symbol category filter").choices([...knownSymbolCategoryList()])).option("--path-prefix <prefix>", "Repository path prefix filter").addOption(new Option3("--intent <intent>", "File intent filter (omit to search across all intents)").choices([
|
|
11920
12977
|
"production",
|
|
@@ -11929,7 +12986,7 @@ function registerSearchCommand(program) {
|
|
|
11929
12986
|
const deps = await loadContainer2();
|
|
11930
12987
|
await searchAction(query, options, deps);
|
|
11931
12988
|
});
|
|
11932
|
-
program.command("search-status").summary("Check status of a
|
|
12989
|
+
program.command("search-status").summary("Check the status of a previous search").description(SEARCH_STATUS_DESCRIPTION).argument("<search-ref>", "Search reference returned by githits search").option("--json", "Output as JSON").action(async (searchRef, options) => {
|
|
11933
12990
|
const deps = await loadContainer2();
|
|
11934
12991
|
await searchStatusAction(searchRef, options, deps);
|
|
11935
12992
|
});
|
|
@@ -11948,7 +13005,7 @@ function requireSearchService(deps) {
|
|
|
11948
13005
|
return deps.codeNavigationService;
|
|
11949
13006
|
}
|
|
11950
13007
|
async function loadContainer2() {
|
|
11951
|
-
const { createContainer: createContainer2 } = await import("./shared/chunk-
|
|
13008
|
+
const { createContainer: createContainer2 } = await import("./shared/chunk-70bn2pmx.js");
|
|
11952
13009
|
return createContainer2();
|
|
11953
13010
|
}
|
|
11954
13011
|
function parseTargetSpecs(specs) {
|
|
@@ -12047,7 +13104,7 @@ function formatUnifiedSearchTerminal(payload) {
|
|
|
12047
13104
|
lines.push("");
|
|
12048
13105
|
lines.push("Partial results:");
|
|
12049
13106
|
}
|
|
12050
|
-
const sourceStatusNotes = formatSourceStatusNotes(payload.sourceStatus);
|
|
13107
|
+
const sourceStatusNotes = formatSourceStatusNotes(payload.sourceStatus, warnings);
|
|
12051
13108
|
if (payload.results.length === 0) {
|
|
12052
13109
|
lines.push("No results.");
|
|
12053
13110
|
if (sourceStatusNotes.length > 0) {
|
|
@@ -12159,13 +13216,17 @@ function formatSearchStatusPartialTerminal(payload) {
|
|
|
12159
13216
|
sourceStatus: payload.result.sourceStatus
|
|
12160
13217
|
});
|
|
12161
13218
|
}
|
|
12162
|
-
function formatSourceStatusNotes(sourceStatus) {
|
|
13219
|
+
function formatSourceStatusNotes(sourceStatus, warnings) {
|
|
12163
13220
|
const useColors = shouldUseColors();
|
|
12164
13221
|
if (!sourceStatus) {
|
|
12165
13222
|
return [];
|
|
12166
13223
|
}
|
|
12167
13224
|
const lines = [];
|
|
12168
13225
|
for (const entry of sourceStatus) {
|
|
13226
|
+
const warningPrefix = `Source '${entry.source.toLowerCase()}' for ${entry.targetLabel}:`;
|
|
13227
|
+
if (warnings?.some((warning2) => warning2.startsWith(warningPrefix))) {
|
|
13228
|
+
continue;
|
|
13229
|
+
}
|
|
12169
13230
|
const label = `${entry.source.toLowerCase()} on ${entry.targetLabel}`;
|
|
12170
13231
|
if (entry.ignoredFilters && entry.ignoredFilters.length > 0) {
|
|
12171
13232
|
lines.push(dim(`Note: ${label} ignored filters: ${entry.ignoredFilters.join(", ")}`, useColors));
|
|
@@ -12396,7 +13457,7 @@ function mergeRanges2(ranges) {
|
|
|
12396
13457
|
}
|
|
12397
13458
|
return merged;
|
|
12398
13459
|
}
|
|
12399
|
-
function formatUnifiedSearchMetadata(entry,
|
|
13460
|
+
function formatUnifiedSearchMetadata(entry, _useColors) {
|
|
12400
13461
|
if (entry.type !== "documentation_page" && entry.type !== "repository_doc") {
|
|
12401
13462
|
return [];
|
|
12402
13463
|
}
|
|
@@ -12409,6 +13470,10 @@ function formatUnifiedSearchMetadata(entry, useColors) {
|
|
|
12409
13470
|
// src/cli.ts
|
|
12410
13471
|
var program = new Command;
|
|
12411
13472
|
var argv = process.argv.slice(2);
|
|
13473
|
+
if (argv.includes("--no-color")) {
|
|
13474
|
+
process.env.NO_COLOR = "1";
|
|
13475
|
+
}
|
|
13476
|
+
var useColors = shouldUseColors();
|
|
12412
13477
|
var commandSpans = new WeakMap;
|
|
12413
13478
|
var createUpdateCheckService = () => new NpmRegistryUpdateCheckService({
|
|
12414
13479
|
currentVersion: version,
|
|
@@ -12445,21 +13510,23 @@ var rootCliPreAction = createRootCliPreAction({
|
|
|
12445
13510
|
clearAuthSessionMetadata: clearAutoLoginAuthSessionMetadata,
|
|
12446
13511
|
loginFlow: (options, deps) => loginFlow(options, deps, stderrLoginOutput)
|
|
12447
13512
|
});
|
|
12448
|
-
program.name("githits").description("
|
|
13513
|
+
program.name("githits").description("Grounded open-source context for AI coding agents").version(version).option("--no-color", "Disable colored output").configureHelp({
|
|
13514
|
+
styleTitle: (title) => colorizeBrand(title, "primary", useColors, { bold: true })
|
|
13515
|
+
}).hook("preAction", async (thisCommand, actionCommand) => {
|
|
12449
13516
|
const command = actionCommand ?? thisCommand;
|
|
12450
13517
|
commandSpans.set(command, startTelemetrySpan(getTelemetryCommandName(command)));
|
|
12451
13518
|
await rootCliPreAction(thisCommand, actionCommand);
|
|
12452
13519
|
}).hook("postAction", (_thisCommand, actionCommand) => {
|
|
12453
13520
|
endTelemetrySpan(commandSpans.get(actionCommand));
|
|
12454
13521
|
}).addHelpText("after", `
|
|
12455
|
-
Getting started:
|
|
12456
|
-
githits init
|
|
12457
|
-
githits login
|
|
13522
|
+
${colorizeBrand("Getting started:", "primary", useColors, { bold: true })}
|
|
13523
|
+
githits init Connect GitHits to your coding agents
|
|
13524
|
+
githits login Sign in to your GitHits account
|
|
12458
13525
|
githits mcp Show MCP setup instructions
|
|
12459
|
-
githits example "query"
|
|
13526
|
+
githits example "query" Find real-world implementations
|
|
12460
13527
|
|
|
12461
13528
|
Learn more at https://githits.com
|
|
12462
|
-
Docs: https://
|
|
13529
|
+
Docs: https://docs.githits.com
|
|
12463
13530
|
Support: support@githits.com`);
|
|
12464
13531
|
registerInitCommand(program);
|
|
12465
13532
|
registerLoginCommand(program);
|