diff --git a/.blueprint/modules/gitea-integration.md b/.blueprint/modules/gitea-integration.md
index 2c225fd..15db23e 100644
--- a/.blueprint/modules/gitea-integration.md
+++ b/.blueprint/modules/gitea-integration.md
@@ -124,8 +124,8 @@ Gitea push → POST /webhook/gitea/:project_id
- complexity: M
- acceptance: TBD —— remote 切换 + 推送,安全增量,不丢历史
-### 📋 G7. 新建仓库后一键接线(本地 init + 首次 push)
-- status: todo
+### ✅ G7. 新建仓库后一键接线(本地 init + 首次 push)
+- status: done
- complexity: M
- depends: G2
- files: src/components/dashboard/GiteaPanel.tsx, src/lib/commands.ts
diff --git a/src/components/dashboard/BlueprintModal.tsx b/src/components/dashboard/BlueprintModal.tsx
index 94e767f..f46909f 100644
--- a/src/components/dashboard/BlueprintModal.tsx
+++ b/src/components/dashboard/BlueprintModal.tsx
@@ -1372,7 +1372,7 @@ function GovernancePanel({
{/* ── Gitea 集成(统一 git 后端)─────────────────────────── */}
- {projectId && }
+ {projectId && }
{/* 项目信息 */}
diff --git a/src/components/dashboard/GiteaPanel.tsx b/src/components/dashboard/GiteaPanel.tsx
index 568330f..0d88be9 100644
--- a/src/components/dashboard/GiteaPanel.tsx
+++ b/src/components/dashboard/GiteaPanel.tsx
@@ -18,6 +18,10 @@ import {
giteaSetupWebhook,
giteaUnlinkRepo,
giteaPollNow,
+ scanLocalGit,
+ gitInitAndCommit,
+ gitRemoteSet,
+ gitPush,
type GiteaInstance,
} from "../../lib/commands";
import { useUIStore } from "../../store/ui";
@@ -186,9 +190,10 @@ export function GiteaInstancesSection() {
interface Props {
projectId: string;
projectName: string;
+ projectPath?: string;
}
-export function GiteaPanel({ projectId, projectName }: Props) {
+export function GiteaPanel({ projectId, projectName, projectPath }: Props) {
const qc = useQueryClient();
const showToast = useUIStore((s) => s.showToast);
@@ -207,6 +212,36 @@ export function GiteaPanel({ projectId, projectName }: Props) {
queryFn: giteaListLinkedRepos,
});
+ // G7 一键接线:绑定后检测本地 git 状态(无 git / 无 origin / 已接线)
+ const { data: localGit, refetch: refetchLocalGit } = useQuery({
+ queryKey: ["gitea-local-git", projectId],
+ queryFn: () => scanLocalGit(projectPath!),
+ enabled: !!link && !!projectPath,
+ staleTime: 30000,
+ retry: false,
+ });
+ const hasOrigin = localGit?.remotes.some((r) => r.name === "origin") ?? false;
+ const needsWiring = !!link && !!projectPath && localGit != null && (!localGit.has_git || !hasOrigin);
+ const [wiring, setWiring] = useState(false);
+
+ const handleWireUp = async () => {
+ if (!projectPath || !link?.clone_url) return;
+ setWiring(true);
+ try {
+ if (!localGit?.has_git || !localGit?.has_commits) {
+ await gitInitAndCommit(projectPath, "chore: 项目初始化(炼境接线)");
+ }
+ await gitRemoteSet(projectPath, link.clone_url);
+ const msg = await gitPush(projectPath);
+ showToast(`✓ 接线完成:${msg}`);
+ refetchLocalGit();
+ } catch (e) {
+ showToast(`❌ 接线失败: ${e}`);
+ } finally {
+ setWiring(false);
+ }
+ };
+
const refresh = () => {
qc.invalidateQueries({ queryKey: ["gitea-link", projectId] });
qc.invalidateQueries({ queryKey: ["gitea-linked-repos"] });
@@ -326,6 +361,32 @@ export function GiteaPanel({ projectId, projectName }: Props) {
+ {/* G7 本地接线:仓库已建但本地未连通时的一次性引导 */}
+ {needsWiring && (
+
+
+ {!localGit?.has_git ? "本地目录还不是 git 仓库" : "本地仓库尚未关联远端 origin"}
+
+
+
+ {!localGit?.has_git
+ ? "将执行:git init + 首次提交 + 关联 origin + 推送"
+ : "将执行:关联 origin + 推送当前分支"}
+
+
+
+
+ 接线是一次性动作;日常 commit / push 在项目自己的 AI 会话中进行。
+
+
+ )}
+
{/* webhook 状态与注册 */}