[Init] Initial commit - NetMesh terminal manager
Some checks failed
build-packages / resolve bundled mosh-client (push) Has been cancelled
build-packages / resolve bundled et-client (push) Has been cancelled
build-packages / build-macos (push) Has been cancelled
build-packages / build-windows (push) Has been cancelled
build-packages / build-linux-x64 (push) Has been cancelled
build-packages / build-linux-arm64 (push) Has been cancelled
build-packages / release (push) Has been cancelled
build-packages / update Nix release metadata (push) Has been cancelled
build-packages / bump homebrew tap (push) Has been cancelled
test / lint-and-test (push) Has been cancelled
AI automation / Route event (push) Has been cancelled
AI automation / Hand reopened issue to maintainers (push) Has been cancelled
AI automation / Clean source issue state (push) Has been cancelled
AI automation / Reconcile handoffs (push) Has been cancelled
AI automation / Classify issue (push) Has been cancelled
AI automation / Claude Code smoke (push) Has been cancelled
AI automation / Review issue follow-up (push) Has been cancelled
AI automation / Publish issue follow-up (push) Has been cancelled
AI automation / Implement with Claude Code (push) Has been cancelled
AI automation / Publish implement PR (push) Has been cancelled
AI automation / Continue queued issue comments (push) Has been cancelled
AI automation / Codex review loop (push) Has been cancelled
AI automation / Publish Codex fix (push) Has been cancelled
AI automation / Clear Codex dispatch marker (push) Has been cancelled
AI automation / Own PR re-request Codex (push) Has been cancelled
AI automation / External PR re-request Codex (push) Has been cancelled
AI automation / Poll Codex reaction / retry (push) Has been cancelled
build-et-binaries / build-linux-x64 (push) Has been cancelled
build-et-binaries / build-linux-arm64 (push) Has been cancelled
build-et-binaries / build-macos-universal (push) Has been cancelled
build-et-binaries / build-windows-x64 (push) Has been cancelled
build-et-binaries / release (push) Has been cancelled
Some checks failed
build-packages / resolve bundled mosh-client (push) Has been cancelled
build-packages / resolve bundled et-client (push) Has been cancelled
build-packages / build-macos (push) Has been cancelled
build-packages / build-windows (push) Has been cancelled
build-packages / build-linux-x64 (push) Has been cancelled
build-packages / build-linux-arm64 (push) Has been cancelled
build-packages / release (push) Has been cancelled
build-packages / update Nix release metadata (push) Has been cancelled
build-packages / bump homebrew tap (push) Has been cancelled
test / lint-and-test (push) Has been cancelled
AI automation / Route event (push) Has been cancelled
AI automation / Hand reopened issue to maintainers (push) Has been cancelled
AI automation / Clean source issue state (push) Has been cancelled
AI automation / Reconcile handoffs (push) Has been cancelled
AI automation / Classify issue (push) Has been cancelled
AI automation / Claude Code smoke (push) Has been cancelled
AI automation / Review issue follow-up (push) Has been cancelled
AI automation / Publish issue follow-up (push) Has been cancelled
AI automation / Implement with Claude Code (push) Has been cancelled
AI automation / Publish implement PR (push) Has been cancelled
AI automation / Continue queued issue comments (push) Has been cancelled
AI automation / Codex review loop (push) Has been cancelled
AI automation / Publish Codex fix (push) Has been cancelled
AI automation / Clear Codex dispatch marker (push) Has been cancelled
AI automation / Own PR re-request Codex (push) Has been cancelled
AI automation / External PR re-request Codex (push) Has been cancelled
AI automation / Poll Codex reaction / retry (push) Has been cancelled
build-et-binaries / build-linux-x64 (push) Has been cancelled
build-et-binaries / build-linux-arm64 (push) Has been cancelled
build-et-binaries / build-macos-universal (push) Has been cancelled
build-et-binaries / build-windows-x64 (push) Has been cancelled
build-et-binaries / release (push) Has been cancelled
This commit is contained in:
134
components/settings/tabs/ai/ToolAccessGuidance.tsx
Normal file
134
components/settings/tabs/ai/ToolAccessGuidance.tsx
Normal file
@@ -0,0 +1,134 @@
|
||||
import React, { useState } from "react";
|
||||
import { Check, Copy } from "lucide-react";
|
||||
import { useI18n } from "../../../../application/i18n/I18nProvider";
|
||||
import type { AIToolIntegrationMode } from "../../../../infrastructure/ai/types";
|
||||
import { cn } from "../../../../lib/utils";
|
||||
import { useToolAccessGuidanceState } from "../../../../application/state/useToolAccessGuidanceState";
|
||||
import { EXTERNAL_MCP_DISCOVERY_ENV_VAR } from "./ExternalMcpCard";
|
||||
|
||||
/** Build a ready-to-paste prompt so an external AI client can register Netcatty MCP itself. */
|
||||
export function buildMcpOnboardingPrompt(
|
||||
launcherPath: string | null | undefined,
|
||||
discoveryPath: string | null | undefined,
|
||||
): string {
|
||||
if (!launcherPath) {
|
||||
return [
|
||||
"Please connect Netcatty to this session via MCP.",
|
||||
"In the Netcatty desktop app, open Settings → AI → Tool Access, turn on External MCP,",
|
||||
"then copy the generated prompt from the Tool Access section and run it here.",
|
||||
"After that, list the netcatty-external MCP tools and call get_environment to verify the connection.",
|
||||
].join(" ");
|
||||
}
|
||||
const lines = [
|
||||
"Please register Netcatty's MCP server in your MCP client configuration:",
|
||||
`- Server name: netcatty-external`,
|
||||
`- Transport: local stdio`,
|
||||
`- Command: ${launcherPath}`,
|
||||
];
|
||||
if (discoveryPath) {
|
||||
lines.push(`- Environment: ${EXTERNAL_MCP_DISCOVERY_ENV_VAR}=${discoveryPath}`);
|
||||
}
|
||||
lines.push(
|
||||
"After registering, list the server's tools and call get_environment to verify the connection.",
|
||||
"Keep the Netcatty desktop app running while you use these tools.",
|
||||
);
|
||||
return lines.join("\n");
|
||||
}
|
||||
|
||||
type CopyRowProps = {
|
||||
value: string;
|
||||
label: string;
|
||||
copyLabel: string;
|
||||
copiedLabel: string;
|
||||
testId?: string;
|
||||
};
|
||||
|
||||
const CopyRow: React.FC<CopyRowProps> = ({ value, label, copyLabel, copiedLabel, testId }) => {
|
||||
const [copied, setCopied] = useState(false);
|
||||
const canCopy = Boolean(value);
|
||||
|
||||
const handleCopy = async () => {
|
||||
if (!value) return;
|
||||
try {
|
||||
await navigator.clipboard.writeText(value);
|
||||
setCopied(true);
|
||||
window.setTimeout(() => setCopied(false), 1200);
|
||||
} catch {
|
||||
// Clipboard may be unavailable; the text stays selectable in the block.
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="space-y-1.5">
|
||||
<div className="text-xs font-medium text-muted-foreground">{label}</div>
|
||||
<div className="group relative rounded-md border border-border/60 bg-muted/20">
|
||||
<pre
|
||||
data-testid={testId}
|
||||
className={cn(
|
||||
"max-h-40 overflow-auto whitespace-pre-wrap break-all px-3 py-2.5 pr-11 font-mono text-xs leading-5",
|
||||
!value && "text-muted-foreground",
|
||||
)}
|
||||
>
|
||||
{value}
|
||||
</pre>
|
||||
<button
|
||||
type="button"
|
||||
disabled={!canCopy}
|
||||
className="absolute right-1.5 top-1.5 flex h-7 w-7 items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-secondary hover:text-foreground disabled:opacity-40"
|
||||
onClick={() => void handleCopy()}
|
||||
aria-label={copied ? copiedLabel : copyLabel}
|
||||
title={copied ? copiedLabel : copyLabel}
|
||||
>
|
||||
{copied ? <Check size={14} className="text-emerald-500" /> : <Copy size={14} />}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const ToolAccessGuidance: React.FC<{ mode: AIToolIntegrationMode }> = ({ mode }) => {
|
||||
const { t } = useI18n();
|
||||
const { skillPath, commandPrefix, mcpLauncherPath, mcpDiscoveryPath } =
|
||||
useToolAccessGuidanceState(mode);
|
||||
|
||||
if (mode === "skills") {
|
||||
return (
|
||||
<div className="rounded-md border border-border/60 bg-background/50 p-3 space-y-2">
|
||||
<p className="text-xs text-muted-foreground leading-5">
|
||||
{t("ai.toolAccess.skills.description")}
|
||||
</p>
|
||||
<CopyRow
|
||||
value={skillPath || ""}
|
||||
label={t("ai.toolAccess.skills.file")}
|
||||
copyLabel={t("ai.externalMcp.copy")}
|
||||
copiedLabel={t("ai.externalMcp.copied")}
|
||||
testId="tool-access-skill-path"
|
||||
/>
|
||||
{!skillPath ? (
|
||||
<p className="text-xs text-amber-500">{t("ai.toolAccess.skills.unavailable")}</p>
|
||||
) : null}
|
||||
{commandPrefix ? (
|
||||
<p className="text-xs text-muted-foreground/80 font-mono break-all">{commandPrefix}</p>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="rounded-md border border-border/60 bg-background/50 p-3 space-y-2">
|
||||
<p className="text-xs text-muted-foreground leading-5">
|
||||
{t("ai.toolAccess.mcpPrompt.description")}
|
||||
</p>
|
||||
<CopyRow
|
||||
value={buildMcpOnboardingPrompt(mcpLauncherPath, mcpDiscoveryPath)}
|
||||
label={t("ai.toolAccess.mcpPrompt.title")}
|
||||
copyLabel={t("ai.externalMcp.copy")}
|
||||
copiedLabel={t("ai.externalMcp.copied")}
|
||||
testId="tool-access-mcp-prompt"
|
||||
/>
|
||||
{!mcpLauncherPath ? (
|
||||
<p className="text-xs text-amber-500">{t("ai.toolAccess.mcpPrompt.enableHint")}</p>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user