githits 0.4.6 → 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/README.md +2 -2
- package/dist/cli.js +758 -251
- package/dist/index.js +1 -1
- package/dist/shared/{chunk-2fdxv7pe.js → chunk-70bn2pmx.js} +2 -2
- package/dist/shared/{chunk-a1hzwt2m.js → chunk-7b4f7fd5.js} +1 -1
- package/dist/shared/{chunk-f16s86ze.js → chunk-tm11qwgr.js} +4 -1
- package/gemini-extension.json +1 -1
- package/package.json +2 -2
- package/plugins/claude/.claude-plugin/plugin.json +1 -1
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}`;
|
|
@@ -5963,7 +6004,7 @@ auth.storage = "file". File storage is plaintext on disk.
|
|
|
5963
6004
|
Use --no-browser in environments without a display (CI, SSH sessions)
|
|
5964
6005
|
to get a URL you can open on another device.`;
|
|
5965
6006
|
function registerLoginCommand(program) {
|
|
5966
|
-
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) => {
|
|
5967
6008
|
const deps = await createAuthCommandDependencies();
|
|
5968
6009
|
await loginAction(options, deps);
|
|
5969
6010
|
});
|
|
@@ -6105,6 +6146,61 @@ function getPostLoginContinuationMessage(command) {
|
|
|
6105
6146
|
return;
|
|
6106
6147
|
}
|
|
6107
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
|
+
};
|
|
6108
6204
|
// src/shared/unified-search-request.ts
|
|
6109
6205
|
var DEFAULT_UNIFIED_SEARCH_LIMIT = 10;
|
|
6110
6206
|
function buildUnifiedSearchParams(input) {
|
|
@@ -6595,7 +6691,30 @@ function labelsDiverge(input) {
|
|
|
6595
6691
|
const served = input.servedTarget;
|
|
6596
6692
|
if (!served)
|
|
6597
6693
|
return false;
|
|
6598
|
-
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
|
+
};
|
|
6599
6718
|
}
|
|
6600
6719
|
function warningForEntry(entry) {
|
|
6601
6720
|
const reasons = [];
|
|
@@ -7650,7 +7769,8 @@ async function pkgFilesAction(firstArg, secondArg, options, deps) {
|
|
|
7650
7769
|
limit,
|
|
7651
7770
|
waitTimeoutMs: wait
|
|
7652
7771
|
});
|
|
7653
|
-
const
|
|
7772
|
+
const spinner = startSpinner(SPINNER_MESSAGES.code, !options.json);
|
|
7773
|
+
const result = await deps.codeNavigationService.listFiles(build.params).finally(() => spinner.stop());
|
|
7654
7774
|
const payload = buildListFilesSuccessPayload(result, {
|
|
7655
7775
|
registry: target.registry ? toPkgseerRegistryLowercase(target.registry) : undefined,
|
|
7656
7776
|
name: target.packageName,
|
|
@@ -7786,7 +7906,8 @@ async function pkgGrepAction(first, second, third, options, deps) {
|
|
|
7786
7906
|
symbolFields: options.symbolField,
|
|
7787
7907
|
waitTimeoutMs: wait
|
|
7788
7908
|
});
|
|
7789
|
-
const
|
|
7909
|
+
const spinner = startSpinner(SPINNER_MESSAGES.code, !options.json);
|
|
7910
|
+
const result = await deps.codeNavigationService.grepRepo(build.params).finally(() => spinner.stop());
|
|
7790
7911
|
const payload = buildGrepRepoSuccessPayload(result, {
|
|
7791
7912
|
registry: target.registry ? toPkgseerRegistryLowercase(target.registry) : undefined,
|
|
7792
7913
|
name: target.packageName,
|
|
@@ -7880,7 +8001,7 @@ grep run. --symbol-field hydrates enclosing symbol metadata (appears under each
|
|
|
7880
8001
|
match in --verbose output; full payload in --json).`;
|
|
7881
8002
|
function registerCodeGrepCommand(pkgCommand) {
|
|
7882
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) => {
|
|
7883
|
-
const { createContainer: createContainer2 } = await import("./shared/chunk-
|
|
8004
|
+
const { createContainer: createContainer2 } = await import("./shared/chunk-70bn2pmx.js");
|
|
7884
8005
|
const deps = await createContainer2();
|
|
7885
8006
|
await pkgGrepAction(arg1, arg2, arg3, options, {
|
|
7886
8007
|
codeNavigationService: deps.codeNavigationService,
|
|
@@ -7990,7 +8111,8 @@ async function pkgReadAction(firstArg, secondArg, options, deps) {
|
|
|
7990
8111
|
endLine: range.endLine,
|
|
7991
8112
|
waitTimeoutMs: wait
|
|
7992
8113
|
});
|
|
7993
|
-
const
|
|
8114
|
+
const spinner = startSpinner(SPINNER_MESSAGES.code, !options.json);
|
|
8115
|
+
const result = await deps.codeNavigationService.readFile(build.params).finally(() => spinner.stop());
|
|
7994
8116
|
const payload = buildReadFileSuccessPayload(result, {
|
|
7995
8117
|
registry: target.registry ? toPkgseerRegistryLowercase(target.registry) : undefined,
|
|
7996
8118
|
name: target.packageName,
|
|
@@ -8131,7 +8253,7 @@ async function registerCodeCommandGroup(program, options = {}) {
|
|
|
8131
8253
|
if (!registration.shouldRegister) {
|
|
8132
8254
|
return;
|
|
8133
8255
|
}
|
|
8134
|
-
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`.");
|
|
8135
8257
|
registerCodeFilesCommand(codeCommand);
|
|
8136
8258
|
registerCodeReadCommand(codeCommand);
|
|
8137
8259
|
registerCodeGrepCommand(codeCommand);
|
|
@@ -8152,7 +8274,8 @@ async function docsListAction(spec, options, deps) {
|
|
|
8152
8274
|
limit,
|
|
8153
8275
|
after: options.after
|
|
8154
8276
|
});
|
|
8155
|
-
const
|
|
8277
|
+
const spinner = startSpinner(SPINNER_MESSAGES.docs, !options.json);
|
|
8278
|
+
const result = await deps.packageIntelligenceService.listPackageDocs(build.params).finally(() => spinner.stop());
|
|
8156
8279
|
const payload = buildListPackageDocsSuccessPayload(result, {
|
|
8157
8280
|
limitExplicit: build.limitExplicit,
|
|
8158
8281
|
afterExplicit: build.afterExplicit,
|
|
@@ -8222,7 +8345,8 @@ async function docsReadAction(pageId, options, deps) {
|
|
|
8222
8345
|
}
|
|
8223
8346
|
const range = options.lines ? parseLinesOption(options.lines) : undefined;
|
|
8224
8347
|
const build = buildReadPackageDocParams({ pageId });
|
|
8225
|
-
const
|
|
8348
|
+
const spinner = startSpinner(SPINNER_MESSAGES.docs, !options.json);
|
|
8349
|
+
const result = await deps.packageIntelligenceService.readPackageDoc(build.params).finally(() => spinner.stop());
|
|
8226
8350
|
const payload = buildReadPackageDocSuccessPayload(result, build.params.pageId, range);
|
|
8227
8351
|
if (options.json) {
|
|
8228
8352
|
console.log(JSON.stringify(payload));
|
|
@@ -8275,7 +8399,7 @@ async function registerDocsCommandGroup(program, options = {}) {
|
|
|
8275
8399
|
if (!registration.shouldRegister) {
|
|
8276
8400
|
return;
|
|
8277
8401
|
}
|
|
8278
|
-
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.");
|
|
8279
8403
|
registerDocsListCommand(docsCommand);
|
|
8280
8404
|
registerDocsReadCommand(docsCommand);
|
|
8281
8405
|
}
|
|
@@ -8288,12 +8412,13 @@ async function exampleAction(query, options, deps) {
|
|
|
8288
8412
|
}
|
|
8289
8413
|
requireAuth(deps);
|
|
8290
8414
|
try {
|
|
8415
|
+
const spinner = startSpinner(SPINNER_MESSAGES.example, !options.json);
|
|
8291
8416
|
const result = await deps.githitsService.search({
|
|
8292
8417
|
query,
|
|
8293
8418
|
language: options.lang,
|
|
8294
8419
|
licenseMode: options.license,
|
|
8295
8420
|
includeExplanation: options.explain
|
|
8296
|
-
});
|
|
8421
|
+
}).finally(() => spinner.stop());
|
|
8297
8422
|
if (options.json) {
|
|
8298
8423
|
const solutionId = extractSolutionId(result);
|
|
8299
8424
|
const payload = solutionId ? { result, solution_id: solutionId } : { result };
|
|
@@ -8328,7 +8453,7 @@ Examples:
|
|
|
8328
8453
|
githits example "react hooks patterns" -l typescript --explain
|
|
8329
8454
|
githits example "react hooks patterns" -l typescript --json`;
|
|
8330
8455
|
function registerExampleCommand(program) {
|
|
8331
|
-
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) => {
|
|
8332
8457
|
try {
|
|
8333
8458
|
const deps = await loadContainer();
|
|
8334
8459
|
await exampleAction(query, options, deps);
|
|
@@ -8340,7 +8465,7 @@ function registerExampleCommand(program) {
|
|
|
8340
8465
|
});
|
|
8341
8466
|
}
|
|
8342
8467
|
async function loadContainer() {
|
|
8343
|
-
const { createContainer: createContainer2 } = await import("./shared/chunk-
|
|
8468
|
+
const { createContainer: createContainer2 } = await import("./shared/chunk-70bn2pmx.js");
|
|
8344
8469
|
return createContainer2();
|
|
8345
8470
|
}
|
|
8346
8471
|
// src/commands/feedback.ts
|
|
@@ -8388,7 +8513,7 @@ Examples:
|
|
|
8388
8513
|
githits feedback --accept --tool code_grep -m "regex is fast on npm:lodash"
|
|
8389
8514
|
githits feedback --reject --tool search -m "missing kotlin support"`;
|
|
8390
8515
|
function registerFeedbackCommand(program) {
|
|
8391
|
-
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) => {
|
|
8392
8517
|
try {
|
|
8393
8518
|
const deps = await createContainer();
|
|
8394
8519
|
await feedbackAction(solutionId, options, deps);
|
|
@@ -8631,20 +8756,6 @@ function removeServerConfig(existingContent, serversKey, serverName) {
|
|
|
8631
8756
|
`
|
|
8632
8757
|
};
|
|
8633
8758
|
}
|
|
8634
|
-
function formatSetupPreview(config) {
|
|
8635
|
-
if (config.method === "composite") {
|
|
8636
|
-
return config.steps.map((step) => formatSetupPreview(step)).join(`
|
|
8637
|
-
`);
|
|
8638
|
-
}
|
|
8639
|
-
if (config.method === "cli") {
|
|
8640
|
-
return config.commands.map((cmd) => `Will run: ${cmd.command} ${cmd.args.join(" ")}`).join(`
|
|
8641
|
-
`);
|
|
8642
|
-
}
|
|
8643
|
-
const snippet = JSON.stringify({ [config.serverName]: config.serverConfig }, null, 2);
|
|
8644
|
-
return `Will add to ${config.configPath}:
|
|
8645
|
-
|
|
8646
|
-
${snippet}`;
|
|
8647
|
-
}
|
|
8648
8759
|
function formatUninstallPreview(config) {
|
|
8649
8760
|
if (config.method === "composite") {
|
|
8650
8761
|
return config.steps.map(({ step }) => formatUninstallPreview(step)).join(`
|
|
@@ -9514,89 +9625,105 @@ var agentDefinitions = [
|
|
|
9514
9625
|
googleAntigravity,
|
|
9515
9626
|
openCode
|
|
9516
9627
|
];
|
|
9517
|
-
async function
|
|
9518
|
-
|
|
9519
|
-
|
|
9520
|
-
|
|
9521
|
-
|
|
9522
|
-
|
|
9523
|
-
|
|
9524
|
-
|
|
9525
|
-
|
|
9526
|
-
if (agent.detectCommand) {
|
|
9527
|
-
try {
|
|
9528
|
-
const resolvedCommand = await agent.detectCommand(execService, fs);
|
|
9529
|
-
if (resolvedCommand) {
|
|
9530
|
-
detected = true;
|
|
9531
|
-
setupContext = { command: resolvedCommand.command };
|
|
9532
|
-
}
|
|
9533
|
-
} catch {
|
|
9534
|
-
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 };
|
|
9535
9637
|
}
|
|
9536
|
-
}
|
|
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;
|
|
9653
|
+
}
|
|
9654
|
+
}
|
|
9655
|
+
} else if (agent.detectionMethod === "hybrid") {
|
|
9656
|
+
let binaryDetected = false;
|
|
9657
|
+
let pathDetected = false;
|
|
9658
|
+
if (agent.detectBinary) {
|
|
9537
9659
|
try {
|
|
9538
|
-
|
|
9660
|
+
binaryDetected = await agent.detectBinary(execService);
|
|
9539
9661
|
} catch {
|
|
9540
|
-
|
|
9662
|
+
binaryDetected = false;
|
|
9541
9663
|
}
|
|
9542
|
-
}
|
|
9664
|
+
}
|
|
9665
|
+
if (!binaryDetected && agent.detectPaths) {
|
|
9543
9666
|
const paths = agent.detectPaths(fs);
|
|
9544
9667
|
for (const path of paths) {
|
|
9545
9668
|
if (await fs.isDirectory(path)) {
|
|
9546
|
-
|
|
9669
|
+
pathDetected = true;
|
|
9547
9670
|
break;
|
|
9548
9671
|
}
|
|
9549
9672
|
}
|
|
9550
|
-
} else if (agent.detectionMethod === "hybrid") {
|
|
9551
|
-
let binaryDetected = false;
|
|
9552
|
-
let pathDetected = false;
|
|
9553
|
-
if (agent.detectBinary) {
|
|
9554
|
-
try {
|
|
9555
|
-
binaryDetected = await agent.detectBinary(execService);
|
|
9556
|
-
} catch {
|
|
9557
|
-
binaryDetected = false;
|
|
9558
|
-
}
|
|
9559
|
-
}
|
|
9560
|
-
if (!binaryDetected && agent.detectPaths) {
|
|
9561
|
-
const paths = agent.detectPaths(fs);
|
|
9562
|
-
for (const path of paths) {
|
|
9563
|
-
if (await fs.isDirectory(path)) {
|
|
9564
|
-
pathDetected = true;
|
|
9565
|
-
break;
|
|
9566
|
-
}
|
|
9567
|
-
}
|
|
9568
|
-
}
|
|
9569
|
-
detected = binaryDetected || pathDetected;
|
|
9570
9673
|
}
|
|
9571
|
-
|
|
9572
|
-
|
|
9573
|
-
|
|
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 };
|
|
9688
|
+
}
|
|
9689
|
+
const checkStatus = await getCliCheckStatus(config.checkCommand, execService);
|
|
9690
|
+
let configured = checkStatus === "configured";
|
|
9691
|
+
if (!configured && checkStatus === "probe_failed") {
|
|
9692
|
+
configured = await isGeminiExtensionInstalledFromFilesystem(fs);
|
|
9574
9693
|
}
|
|
9575
|
-
|
|
9576
|
-
|
|
9577
|
-
|
|
9578
|
-
resolvedSetupConfig: config,
|
|
9579
|
-
resolvedSetupContext: setupContext
|
|
9694
|
+
return {
|
|
9695
|
+
status: configured ? "already_configured" : "needs_setup",
|
|
9696
|
+
agent: scannedAgent
|
|
9580
9697
|
};
|
|
9581
|
-
|
|
9582
|
-
|
|
9583
|
-
|
|
9584
|
-
|
|
9585
|
-
|
|
9586
|
-
|
|
9587
|
-
|
|
9588
|
-
|
|
9589
|
-
|
|
9590
|
-
|
|
9591
|
-
|
|
9592
|
-
|
|
9593
|
-
|
|
9594
|
-
|
|
9595
|
-
|
|
9596
|
-
|
|
9597
|
-
|
|
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);
|
|
9598
9725
|
} else {
|
|
9599
|
-
result.
|
|
9726
|
+
result.notDetected.push(outcome.agent);
|
|
9600
9727
|
}
|
|
9601
9728
|
}
|
|
9602
9729
|
return result;
|
|
@@ -9606,6 +9733,17 @@ class ExitPromptError extends Error {
|
|
|
9606
9733
|
name = "ExitPromptError";
|
|
9607
9734
|
}
|
|
9608
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
|
+
}
|
|
9609
9747
|
function getResolvedSetupConfig(agent, fileSystemService) {
|
|
9610
9748
|
return agent.resolvedSetupConfig ?? agent.getSetupConfig(fileSystemService, agent.resolvedSetupContext);
|
|
9611
9749
|
}
|
|
@@ -9620,14 +9758,415 @@ function getPiConfigFileUninstall(agent, fileSystemService) {
|
|
|
9620
9758
|
const configStep = uninstallConfig.steps.map(({ step }) => step).find((step) => step.method === "config-file");
|
|
9621
9759
|
return configStep ?? null;
|
|
9622
9760
|
}
|
|
9761
|
+
function formatCommand(command, useColors) {
|
|
9762
|
+
return colorizeBrand(command, "secondary", useColors, { bold: true });
|
|
9763
|
+
}
|
|
9623
9764
|
function printReadyNextSteps() {
|
|
9624
|
-
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.");
|
|
9625
10067
|
console.log();
|
|
9626
|
-
console.log("
|
|
9627
|
-
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.");
|
|
9628
10069
|
console.log();
|
|
9629
|
-
|
|
9630
|
-
|
|
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
|
+
}
|
|
9631
10170
|
}
|
|
9632
10171
|
async function verifyAgentConfigured(agent, fileSystemService, execService) {
|
|
9633
10172
|
const postCheck = await scanAgents([agent], fileSystemService, execService);
|
|
@@ -9764,126 +10303,100 @@ async function scanAgentsForUninstall(fileSystemService, execService) {
|
|
|
9764
10303
|
async function initAction(options, deps) {
|
|
9765
10304
|
const useColors = shouldUseColors();
|
|
9766
10305
|
const { fileSystemService, promptService, execService, createLoginDeps } = deps;
|
|
9767
|
-
|
|
9768
|
-
|
|
9769
|
-
|
|
9770
|
-
`);
|
|
9771
|
-
if (!options.skipLogin && createLoginDeps) {
|
|
9772
|
-
console.log(` Checking authentication...
|
|
9773
|
-
`);
|
|
9774
|
-
let loginResult;
|
|
10306
|
+
printInitIntro(useColors);
|
|
10307
|
+
if (!options.yes) {
|
|
10308
|
+
let intent;
|
|
9775
10309
|
try {
|
|
9776
|
-
|
|
9777
|
-
|
|
9778
|
-
|
|
9779
|
-
|
|
9780
|
-
|
|
9781
|
-
}
|
|
9782
|
-
if (loginResult.status === "already_authenticated") {
|
|
9783
|
-
console.log(` ${success("Already authenticated", useColors)}
|
|
9784
|
-
`);
|
|
9785
|
-
} else if (loginResult.status === "success") {
|
|
9786
|
-
console.log(` ${success("Logged in successfully", useColors)}
|
|
9787
|
-
`);
|
|
9788
|
-
} else {
|
|
9789
|
-
console.log(` ${warning(`Login failed: ${loginResult.message}`, useColors)}
|
|
9790
|
-
`);
|
|
9791
|
-
printAuthRecoveryHint();
|
|
9792
|
-
if (!options.yes) {
|
|
9793
|
-
try {
|
|
9794
|
-
const choice = await promptService.confirm3("Continue without authentication?");
|
|
9795
|
-
if (choice === "no") {
|
|
9796
|
-
console.log("\n Setup cancelled. Run `githits login` to authenticate.\n");
|
|
9797
|
-
return;
|
|
9798
|
-
}
|
|
9799
|
-
} catch (err) {
|
|
9800
|
-
if (err instanceof ExitPromptError) {
|
|
9801
|
-
console.log(`
|
|
9802
|
-
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.
|
|
9803
10315
|
`);
|
|
9804
|
-
|
|
9805
|
-
}
|
|
9806
|
-
throw err;
|
|
9807
|
-
}
|
|
10316
|
+
return;
|
|
9808
10317
|
}
|
|
9809
|
-
|
|
9810
|
-
|
|
9811
|
-
|
|
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;
|
|
9812
10327
|
}
|
|
9813
10328
|
}
|
|
9814
|
-
|
|
9815
|
-
|
|
9816
|
-
const
|
|
9817
|
-
|
|
9818
|
-
|
|
9819
|
-
|
|
9820
|
-
|
|
9821
|
-
|
|
9822
|
-
|
|
9823
|
-
for (const agent of scan.notDetected) {
|
|
9824
|
-
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();
|
|
9825
10338
|
}
|
|
9826
|
-
|
|
10339
|
+
printScanSummary(scan, useColors);
|
|
9827
10340
|
if (scan.needsSetup.length === 0 && scan.alreadyConfigured.length === 0) {
|
|
9828
|
-
|
|
9829
|
-
|
|
10341
|
+
printTask("warning", "No supported AI coding tools detected", "install a supported tool and run `githits init` again", useColors);
|
|
10342
|
+
console.log();
|
|
9830
10343
|
return;
|
|
9831
10344
|
}
|
|
9832
|
-
|
|
9833
|
-
|
|
9834
|
-
|
|
9835
|
-
|
|
9836
|
-
|
|
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;
|
|
9837
10358
|
}
|
|
9838
|
-
|
|
9839
|
-
|
|
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);
|
|
9840
10367
|
console.log();
|
|
9841
10368
|
return;
|
|
9842
10369
|
}
|
|
9843
|
-
|
|
9844
|
-
const
|
|
9845
|
-
|
|
9846
|
-
|
|
9847
|
-
|
|
9848
|
-
|
|
9849
|
-
|
|
9850
|
-
|
|
9851
|
-
|
|
9852
|
-
`)) {
|
|
9853
|
-
console.log(` ${line}`);
|
|
9854
|
-
}
|
|
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);
|
|
9855
10379
|
console.log();
|
|
9856
|
-
|
|
9857
|
-
|
|
9858
|
-
|
|
9859
|
-
|
|
9860
|
-
|
|
9861
|
-
|
|
9862
|
-
|
|
9863
|
-
|
|
9864
|
-
|
|
9865
|
-
|
|
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
|
+
};
|
|
9866
10396
|
}
|
|
9867
|
-
throw err;
|
|
9868
|
-
}
|
|
9869
|
-
if (choice === "no") {
|
|
9870
|
-
outcomes.push({ id: agent.id, name: agent.name, status: "skipped" });
|
|
9871
|
-
console.log();
|
|
9872
|
-
continue;
|
|
9873
|
-
}
|
|
9874
|
-
if (choice === "always") {
|
|
9875
|
-
alwaysMode = true;
|
|
9876
|
-
}
|
|
9877
|
-
}
|
|
9878
|
-
let result = config.method === "cli" ? await executeCliSetup(config, execService) : config.method === "config-file" ? await executeConfigFileSetup(config, fileSystemService) : await executeCompositeSetup(config, fileSystemService, execService);
|
|
9879
|
-
if (result.status === "success" || result.status === "already_configured") {
|
|
9880
|
-
const verification = await verifyAgentConfigured(agent, fileSystemService, execService);
|
|
9881
|
-
if (!verification.ok) {
|
|
9882
|
-
result = {
|
|
9883
|
-
status: "failed",
|
|
9884
|
-
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.`
|
|
9885
|
-
};
|
|
9886
10397
|
}
|
|
10398
|
+
} finally {
|
|
10399
|
+
finishTask();
|
|
9887
10400
|
}
|
|
9888
10401
|
outcomes.push({
|
|
9889
10402
|
id: agent.id,
|
|
@@ -9892,38 +10405,30 @@ async function initAction(options, deps) {
|
|
|
9892
10405
|
message: result.status === "failed" ? result.message : undefined
|
|
9893
10406
|
});
|
|
9894
10407
|
if (result.status === "success") {
|
|
9895
|
-
|
|
9896
|
-
`);
|
|
10408
|
+
printTask("success", agent.name, "configured and verified", useColors);
|
|
9897
10409
|
} else if (result.status === "already_configured") {
|
|
9898
|
-
|
|
9899
|
-
`);
|
|
10410
|
+
printTask("warning", agent.name, "already configured", useColors);
|
|
9900
10411
|
} else {
|
|
9901
|
-
|
|
9902
|
-
`);
|
|
10412
|
+
printTask("failed", agent.name, result.message, useColors);
|
|
9903
10413
|
}
|
|
9904
10414
|
}
|
|
10415
|
+
console.log();
|
|
9905
10416
|
const configured = outcomes.filter((o) => o.status === "success").length;
|
|
9906
10417
|
const alreadyDone = outcomes.filter((o) => o.status === "already_configured").length + scan.alreadyConfigured.length;
|
|
9907
10418
|
const failed = outcomes.filter((o) => o.status === "failed").length;
|
|
9908
|
-
const skipped = outcomes.filter((o) => o.status === "skipped").length;
|
|
9909
10419
|
if (failed > 0) {
|
|
9910
10420
|
console.log(" Setup completed with errors.");
|
|
9911
|
-
} else if (continuedWithoutAuth && (configured > 0 || alreadyDone > 0)) {
|
|
9912
|
-
console.log(" MCP is configured, but authentication is still required.");
|
|
9913
|
-
console.log(" Run `githits login` before using GitHits tools.");
|
|
9914
10421
|
} else if (configured > 0 || alreadyDone > 0) {
|
|
9915
|
-
|
|
9916
|
-
} else if (skipped > 0) {
|
|
9917
|
-
console.log(" Setup skipped.");
|
|
10422
|
+
printPostSetupNextSteps(authStatus, useColors);
|
|
9918
10423
|
}
|
|
9919
10424
|
if (failed > 0) {
|
|
9920
|
-
console.log(` ${failed}
|
|
10425
|
+
console.log(` ${failed} tool${failed !== 1 ? "s" : ""} failed to configure.`);
|
|
9921
10426
|
for (const outcome of outcomes.filter((o) => o.status === "failed")) {
|
|
9922
10427
|
console.log(` - ${outcome.name}: ${outcome.message ?? "Unknown error"}`);
|
|
9923
10428
|
}
|
|
9924
10429
|
}
|
|
9925
|
-
if (
|
|
9926
|
-
console.log(` ${
|
|
10430
|
+
if (scan.alreadyConfigured.length > 0) {
|
|
10431
|
+
console.log(` ${scan.alreadyConfigured.length} tool${scan.alreadyConfigured.length !== 1 ? "s" : ""} already configured.`);
|
|
9927
10432
|
}
|
|
9928
10433
|
console.log();
|
|
9929
10434
|
}
|
|
@@ -9931,7 +10436,8 @@ async function initUninstallAction(options, deps) {
|
|
|
9931
10436
|
const useColors = shouldUseColors();
|
|
9932
10437
|
const { fileSystemService, promptService, execService } = deps;
|
|
9933
10438
|
console.log(`
|
|
9934
|
-
${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)}
|
|
9935
10441
|
`);
|
|
9936
10442
|
console.log(` Scanning for configured agents...
|
|
9937
10443
|
`);
|
|
@@ -10068,33 +10574,27 @@ async function initUninstallAction(options, deps) {
|
|
|
10068
10574
|
}
|
|
10069
10575
|
console.log();
|
|
10070
10576
|
}
|
|
10071
|
-
function printAuthRecoveryHint() {
|
|
10577
|
+
function printAuthRecoveryHint(useColors) {
|
|
10072
10578
|
console.log(" You can still configure MCP, but GitHits tools will require auth.");
|
|
10073
10579
|
console.log(" Recovery steps:");
|
|
10074
|
-
console.log("
|
|
10075
|
-
console.log("
|
|
10580
|
+
console.log(` ${formatCommand("githits auth status", useColors)}`);
|
|
10581
|
+
console.log(` ${formatCommand("githits login --force", useColors)}`);
|
|
10076
10582
|
console.log(" For CI or locked-down machines, set GITHITS_API_TOKEN.");
|
|
10077
10583
|
console.log(` If your system keychain is unavailable, set GITHITS_AUTH_STORAGE=file after accepting plaintext storage.
|
|
10078
10584
|
`);
|
|
10079
10585
|
}
|
|
10080
|
-
var INIT_DESCRIPTION = `
|
|
10586
|
+
var INIT_DESCRIPTION = `Connect GitHits to your coding agents.
|
|
10081
10587
|
|
|
10082
|
-
|
|
10083
|
-
|
|
10084
|
-
|
|
10085
|
-
and sets up unconfigured ones with your confirmation.
|
|
10086
|
-
|
|
10087
|
-
Supports CLI-based setup (Claude Code, Codex, Gemini CLI), Pi package setup,
|
|
10088
|
-
and config
|
|
10089
|
-
file editing (Cursor, Windsurf, VS Code, Cline, Claude Desktop,
|
|
10090
|
-
Google Antigravity) with atomic writes.`;
|
|
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.`;
|
|
10091
10591
|
var INIT_UNINSTALL_DESCRIPTION = `Remove GitHits MCP server configuration from your coding agents.
|
|
10092
10592
|
|
|
10093
10593
|
Scans for available agents that currently have GitHits configured, then removes
|
|
10094
10594
|
only the GitHits MCP/plugin configuration with your confirmation. Authentication
|
|
10095
10595
|
tokens are not removed; use \`githits logout\` to remove stored credentials.`;
|
|
10096
10596
|
function registerInitCommand(program) {
|
|
10097
|
-
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) => {
|
|
10098
10598
|
const fileSystemService = new FileSystemServiceImpl;
|
|
10099
10599
|
const promptService = new PromptServiceImpl;
|
|
10100
10600
|
const execService = new ExecServiceImpl;
|
|
@@ -11807,7 +12307,7 @@ function showMcpSetupInstructions() {
|
|
|
11807
12307
|
console.log("Learn more at https://githits.com");
|
|
11808
12308
|
}
|
|
11809
12309
|
function registerMcpCommand(program) {
|
|
11810
|
-
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.
|
|
11811
12311
|
|
|
11812
12312
|
When run interactively (TTY), shows setup instructions.
|
|
11813
12313
|
When run via stdio (non-TTY), starts the MCP server.
|
|
@@ -12379,7 +12879,7 @@ async function registerPkgCommandGroup(program, options = {}) {
|
|
|
12379
12879
|
if (!registration.shouldRegister) {
|
|
12380
12880
|
return;
|
|
12381
12881
|
}
|
|
12382
|
-
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`.");
|
|
12383
12883
|
registerPkgInfoCommand(pkgCommand);
|
|
12384
12884
|
registerPkgVulnsCommand(pkgCommand);
|
|
12385
12885
|
registerPkgDepsCommand(pkgCommand);
|
|
@@ -12408,7 +12908,8 @@ async function searchAction(query, options, deps) {
|
|
|
12408
12908
|
offset: parseOptionalInt(options.offset, "--offset", 0),
|
|
12409
12909
|
waitTimeoutMs: parseWaitMs(options.wait)
|
|
12410
12910
|
});
|
|
12411
|
-
const
|
|
12911
|
+
const spinner = startSpinner(SPINNER_MESSAGES.search, !options.json);
|
|
12912
|
+
const outcome = await service.search(built.params).finally(() => spinner.stop());
|
|
12412
12913
|
const payload = buildUnifiedSearchSuccessPayload(built.params, built.rawQuery, built.compiledQuery, outcome);
|
|
12413
12914
|
if (options.json) {
|
|
12414
12915
|
console.log(JSON.stringify(payload));
|
|
@@ -12470,7 +12971,7 @@ Pass the searchRef returned by githits search when the initial request could
|
|
|
12470
12971
|
not complete within the wait window. This can return progress, partial hits when
|
|
12471
12972
|
the original request used --allow-partial, or final results.`;
|
|
12472
12973
|
function registerSearchCommand(program) {
|
|
12473
|
-
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([
|
|
12474
12975
|
...knownSymbolKindList()
|
|
12475
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([
|
|
12476
12977
|
"production",
|
|
@@ -12485,7 +12986,7 @@ function registerSearchCommand(program) {
|
|
|
12485
12986
|
const deps = await loadContainer2();
|
|
12486
12987
|
await searchAction(query, options, deps);
|
|
12487
12988
|
});
|
|
12488
|
-
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) => {
|
|
12489
12990
|
const deps = await loadContainer2();
|
|
12490
12991
|
await searchStatusAction(searchRef, options, deps);
|
|
12491
12992
|
});
|
|
@@ -12504,7 +13005,7 @@ function requireSearchService(deps) {
|
|
|
12504
13005
|
return deps.codeNavigationService;
|
|
12505
13006
|
}
|
|
12506
13007
|
async function loadContainer2() {
|
|
12507
|
-
const { createContainer: createContainer2 } = await import("./shared/chunk-
|
|
13008
|
+
const { createContainer: createContainer2 } = await import("./shared/chunk-70bn2pmx.js");
|
|
12508
13009
|
return createContainer2();
|
|
12509
13010
|
}
|
|
12510
13011
|
function parseTargetSpecs(specs) {
|
|
@@ -12969,6 +13470,10 @@ function formatUnifiedSearchMetadata(entry, _useColors) {
|
|
|
12969
13470
|
// src/cli.ts
|
|
12970
13471
|
var program = new Command;
|
|
12971
13472
|
var argv = process.argv.slice(2);
|
|
13473
|
+
if (argv.includes("--no-color")) {
|
|
13474
|
+
process.env.NO_COLOR = "1";
|
|
13475
|
+
}
|
|
13476
|
+
var useColors = shouldUseColors();
|
|
12972
13477
|
var commandSpans = new WeakMap;
|
|
12973
13478
|
var createUpdateCheckService = () => new NpmRegistryUpdateCheckService({
|
|
12974
13479
|
currentVersion: version,
|
|
@@ -13005,21 +13510,23 @@ var rootCliPreAction = createRootCliPreAction({
|
|
|
13005
13510
|
clearAuthSessionMetadata: clearAutoLoginAuthSessionMetadata,
|
|
13006
13511
|
loginFlow: (options, deps) => loginFlow(options, deps, stderrLoginOutput)
|
|
13007
13512
|
});
|
|
13008
|
-
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) => {
|
|
13009
13516
|
const command = actionCommand ?? thisCommand;
|
|
13010
13517
|
commandSpans.set(command, startTelemetrySpan(getTelemetryCommandName(command)));
|
|
13011
13518
|
await rootCliPreAction(thisCommand, actionCommand);
|
|
13012
13519
|
}).hook("postAction", (_thisCommand, actionCommand) => {
|
|
13013
13520
|
endTelemetrySpan(commandSpans.get(actionCommand));
|
|
13014
13521
|
}).addHelpText("after", `
|
|
13015
|
-
Getting started:
|
|
13016
|
-
githits init
|
|
13017
|
-
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
|
|
13018
13525
|
githits mcp Show MCP setup instructions
|
|
13019
|
-
githits example "query"
|
|
13526
|
+
githits example "query" Find real-world implementations
|
|
13020
13527
|
|
|
13021
13528
|
Learn more at https://githits.com
|
|
13022
|
-
Docs: https://
|
|
13529
|
+
Docs: https://docs.githits.com
|
|
13023
13530
|
Support: support@githits.com`);
|
|
13024
13531
|
registerInitCommand(program);
|
|
13025
13532
|
registerLoginCommand(program);
|