@unifan/pi-unifan-zh 1.0.10 → 1.0.12

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.
@@ -3,6 +3,7 @@ import {
3
3
  commitWithMsg,
4
4
  getChangedFiles,
5
5
  getStagedDiff,
6
+ getUnpushedCommits,
6
7
  getUnstagedDiff,
7
8
  isGitRepo,
8
9
  pushCurrentBranch,
@@ -114,7 +115,28 @@ export default function (pi: ExtensionAPI) {
114
115
  const changedFiles = await getChangedFiles(ctx.cwd);
115
116
 
116
117
  if (!stagedDiff && !unstagedDiff) {
117
- notify("当前工作区没有任何修改,无需提交。", "info");
118
+ if (andPush) {
119
+ const unpushed = await getUnpushedCommits(ctx.cwd);
120
+ if (unpushed.length > 0) {
121
+ notify(`当前工作区干净,但检测到有 ${unpushed.length} 个未推送提交,正在推送到远端...`, "info");
122
+ const pushRes = await pushCurrentBranch(ctx.cwd, (msg) => notify(msg, "info"));
123
+ if (pushRes.ok) {
124
+ const successMsg = pushRes.autoRebased
125
+ ? `🚀 已自动完成变基 (pull --rebase),并成功将 ${unpushed.length} 个本地提交推送至远端!`
126
+ : `🚀 成功将本地未推送的 ${unpushed.length} 个提交推送至远端!`;
127
+ notify(successMsg, "info");
128
+ pi.sendMessage({
129
+ customType: "pi-commit-result",
130
+ content: `### 🚀 远端推送完成\n\n当前工作区无未提交的修改,已成功将本地 **${unpushed.length} 个历史提交** 推送至远端分支:\n\n\`\`\`text\n${unpushed.join("\n")}\n\`\`\`\n\n${successMsg}`,
131
+ display: true,
132
+ });
133
+ } else {
134
+ notify(`⚠️ 推送失败: ${pushRes.output}`, "error");
135
+ }
136
+ return;
137
+ }
138
+ }
139
+ notify("当前工作区干净,且所有本地提交均已同步至远端,无需提交和推送。", "info");
118
140
  return;
119
141
  }
120
142
 
@@ -178,7 +200,7 @@ export default function (pi: ExtensionAPI) {
178
200
  });
179
201
 
180
202
  pi.registerCommand("commit-push", {
181
- description: "智能 Git 提交并推流:生成标准中文 Commit 后自动提交并执行 git push (支持自动变基重试)",
203
+ description: "智能 Git 提交并推流:生成标准中文 Commit 后自动提交并执行 git push (支持自动变基与未推送提交自动推流)",
182
204
  handler: async (args, ctx) => {
183
205
  await handleCommitCommand(args, ctx, true);
184
206
  },
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@unifan/pi-commit-zh",
3
- "version": "1.0.9",
3
+ "version": "1.0.11",
4
4
  "description": "Pi 智能 Git 提交助手(规范化 Conventional Commits 纯中文版,支持一键提审与推流)",
5
5
  "type": "module",
6
6
  "main": "index.ts",
@@ -45,6 +45,27 @@ export async function getChangedFiles(cwd: string): Promise<string[]> {
45
45
  .filter(Boolean);
46
46
  }
47
47
 
48
+ export async function getUnpushedCommits(cwd: string): Promise<string[]> {
49
+ const res = await runGit(cwd, ["log", "@{u}..HEAD", "--oneline"]);
50
+ if (res.ok && res.stdout.trim()) {
51
+ return res.stdout.trim().split("\n").filter(Boolean);
52
+ }
53
+ const statusRes = await runGit(cwd, ["status", "--porcelain=v1", "-b"]);
54
+ if (statusRes.ok) {
55
+ const match = statusRes.stdout.match(/\[ahead\s+(\d+)\]/);
56
+ if (match && match[1]) {
57
+ const count = parseInt(match[1], 10);
58
+ if (count > 0) {
59
+ const logRes = await runGit(cwd, ["log", "-n", String(count), "--oneline"]);
60
+ if (logRes.ok && logRes.stdout.trim()) {
61
+ return logRes.stdout.trim().split("\n").filter(Boolean);
62
+ }
63
+ }
64
+ }
65
+ }
66
+ return [];
67
+ }
68
+
48
69
  export async function stageAll(cwd: string): Promise<boolean> {
49
70
  const res = await runGit(cwd, ["add", "-A"]);
50
71
  return res.ok;
@@ -59,12 +80,22 @@ export async function pushCurrentBranch(
59
80
  cwd: string,
60
81
  onLog?: (msg: string) => void,
61
82
  ): Promise<{ ok: boolean; output: string; autoRebased?: boolean }> {
62
- const initialPush = await runGit(cwd, ["push"]);
83
+ let initialPush = await runGit(cwd, ["push"]);
63
84
  if (initialPush.ok) {
64
85
  return { ok: true, output: initialPush.stdout || "推送成功" };
65
86
  }
66
87
 
67
- const pushError = `${initialPush.stderr} ${initialPush.stdout}`.toLowerCase();
88
+ let pushError = `${initialPush.stderr} ${initialPush.stdout}`.toLowerCase();
89
+
90
+ if (pushError.includes("no upstream branch") || pushError.includes("set-upstream")) {
91
+ if (onLog) onLog("未关联远端分支,正在自动关联并推送 (git push -u origin HEAD)...");
92
+ const setUpstream = await runGit(cwd, ["push", "-u", "origin", "HEAD"]);
93
+ if (setUpstream.ok) {
94
+ return { ok: true, output: setUpstream.stdout || "推送成功" };
95
+ }
96
+ pushError = `${setUpstream.stderr} ${setUpstream.stdout}`.toLowerCase();
97
+ }
98
+
68
99
  const needsPull =
69
100
  pushError.includes("fetch first") ||
70
101
  pushError.includes("non-fast-forward") ||
@@ -25,11 +25,12 @@ export default function (pi: ExtensionAPI) {
25
25
  registerReviewReportTool(pi);
26
26
  registerPiReviewRenderer(pi);
27
27
  pi.registerCommand("review", {
28
- description: "启动 AI 并发代码审查 (支持多专家子代理 + 门禁总裁判系统)。--lite = 极速单专家审查,--perf = 性能与基准测试审查。",
28
+ description: "启动日常 AI 代码审查 (3 大核心专家 + 门禁裁判长)。--lite 极速单兵,--perf 性能审查,--full 全量会诊。",
29
29
  getArgumentCompletions: (prefix: string) => {
30
30
  const options = [
31
- { value: "--lite", label: "--lite", description: "极速单专家审查 (无门禁,低延迟省 Token)" },
31
+ { value: "--lite", label: "--lite", description: "极速单专家审查 (无门禁,低延迟极省 Token)" },
32
32
  { value: "--perf", label: "--perf", description: "专项性能与基准测试审查 (GC/内存分配/CPU/Benchmark)" },
33
+ { value: "--full", label: "--full", description: "全量 6 专家深度会诊 (Bugbot/安全/合规/历史/注释/性能 + 门禁)" },
33
34
  { value: "--gate-model", label: "--gate-model", description: "指定当前审查的门禁裁判模型" },
34
35
  ];
35
36
  const trimmed = prefix.trimStart();
@@ -69,6 +70,7 @@ export default function (pi: ExtensionAPI) {
69
70
  input: parsed.input,
70
71
  lite: parsed.lite,
71
72
  perf: parsed.perf,
73
+ full: parsed.full,
72
74
  gateModel: parsed.gateModel,
73
75
  });
74
76
  if (!prepared) {
@@ -139,6 +141,30 @@ export default function (pi: ExtensionAPI) {
139
141
  },
140
142
  });
141
143
 
144
+ pi.registerCommand("review-full", {
145
+ description: "全量 6 专家深度代码审查会诊 (Bugbot/安全/合规/历史/注释/性能 + 门禁总裁判)",
146
+ handler: async (args, ctx) => {
147
+ const notify = (msg: string, level: "info" | "warning" | "error" = "info") => {
148
+ if (ctx.hasUI) ctx.ui.notify(msg, level);
149
+ else console.log(`pi-review: ${msg}`);
150
+ };
151
+ try {
152
+ const { config, legacyWarnings } = loadConfig();
153
+ for (const w of legacyWarnings) notify(`pi-review 提示: ${w}`, "warning");
154
+ pi.sendMessage({ customType: "pi-review", content: args ? `/review-full ${args}` : "/review-full", display: true });
155
+ const prepared = await prepareRun({ cwd: ctx.cwd, input: args, full: true });
156
+ if (!prepared) {
157
+ notify("没有检测到需要审查的内容 (未找到修改、PR 或非 Git 仓库)。", "info");
158
+ return;
159
+ }
160
+ pi.sendMessage({ customType: "pi-review-directive", content: prepared.directiveText, display: false }, { triggerTurn: true });
161
+ } catch (err) {
162
+ const message = err instanceof Error ? err.message : String(err);
163
+ notify(`全量代码审查启动失败: ${message}`, "error");
164
+ }
165
+ },
166
+ });
167
+
142
168
  pi.registerCommand("review-config", {
143
169
  description: "编辑代码审查配置 (~/.pi/agent/pi-review.json)",
144
170
  handler: async (_args, ctx) => {
@@ -11,6 +11,8 @@ export interface ParsedReviewArgs {
11
11
  lite: boolean;
12
12
  /** Dedicated performance review mode: perf-reviewer with benchmark capabilities. */
13
13
  perf: boolean;
14
+ /** Full multi-agent review mode: all 6 reviewers + gate. */
15
+ full: boolean;
14
16
  /** Override the gate model for this run (otherwise config.gate.model). */
15
17
  gateModel?: string;
16
18
  }
@@ -24,7 +26,7 @@ const LEGACY_VALUED_FLAGS = new Set([
24
26
 
25
27
  export function parseReviewArgs(raw: string): ParsedReviewArgs {
26
28
  const tokens = tokenize(raw);
27
- const result: ParsedReviewArgs = { noSpawn: false, lite: false, perf: false };
29
+ const result: ParsedReviewArgs = { noSpawn: false, lite: false, perf: false, full: false };
28
30
  const inputParts: string[] = [];
29
31
 
30
32
  for (let i = 0; i < tokens.length; i++) {
@@ -45,6 +47,10 @@ export function parseReviewArgs(raw: string): ParsedReviewArgs {
45
47
  result.perf = true;
46
48
  continue;
47
49
  }
50
+ if (t === "--full") {
51
+ result.full = true;
52
+ continue;
53
+ }
48
54
  if (t === "--gate-model") {
49
55
  const id = tokens[++i];
50
56
  if (id) result.gateModel = id;
@@ -55,7 +55,7 @@ export const DEFAULT_CONFIG: PiReviewConfig = {
55
55
  "history-context": {
56
56
  id: "history-context",
57
57
  label: "History Context",
58
- enabled: true,
58
+ enabled: false,
59
59
  model: "inherit",
60
60
  },
61
61
  "security-review": {
@@ -67,7 +67,7 @@ export const DEFAULT_CONFIG: PiReviewConfig = {
67
67
  "code-comments": {
68
68
  id: "code-comments",
69
69
  label: "Code Comments",
70
- enabled: true,
70
+ enabled: false,
71
71
  model: "inherit",
72
72
  },
73
73
  conventions: {
@@ -237,9 +237,9 @@ export function buildWorkflowScript(input: {
237
237
  const READ_ONLY_PREFIX =
238
238
  "只读任务(READ-ONLY)——仅执行审查分析与基准测试。严禁修改源码文件。仅返回审查发现。所有分析总结、问题描述与建议必须使用纯正中文。";
239
239
 
240
- const lines: string[] = [];
241
- lines.push("");
242
- lines.push("const reviewers = await runs.all([");
240
+ lines.push("let reviewers;");
241
+ lines.push("try {");
242
+ lines.push(" reviewers = await runs.all([");
243
243
  for (const r of reviewers) {
244
244
  const tb = LEAN_BUDGETS.defaultToolBudget;
245
245
  const tbForId = r.id === "history-context" ? LEAN_BUDGETS.historyToolBudget : tb;
@@ -303,7 +303,44 @@ export function buildWorkflowScript(input: {
303
303
  );
304
304
  lines.push(" },");
305
305
  }
306
- lines.push("]);");
306
+ lines.push(" ]);");
307
+ lines.push("} catch (firstErr) {");
308
+ lines.push(" // 自动网络重试保护:若首次并发因网络波动超时,自动延迟800ms后重试一次");
309
+ lines.push(" await new Promise((resolve) => setTimeout(resolve, 800));");
310
+ lines.push(" reviewers = await runs.all([");
311
+ for (const r of reviewers) {
312
+ const tb = LEAN_BUDGETS.defaultToolBudget;
313
+ const tbForId = r.id === "history-context" ? LEAN_BUDGETS.historyToolBudget : tb;
314
+ const taskParts = [
315
+ READ_ONLY_PREFIX,
316
+ `读取 ${JSON.stringify(diffPath)} 作为改动内容——diff 是权威的修改记录,工作区文件仅作上下文参考。所有问题描述必须使用中文。`,
317
+ `你的当前工作区为目标工作区 (${JSON.stringify(workspacePath)})。在此目录下执行必要的 read/grep。`,
318
+ "在额度内完成分析;最终回复必须输出格式规范的 Markdown 审查报告(包含中文 Summary / Findings / Coverage 章节)并停止。所有问题描述、证据引用和总结必须使用纯正中文。",
319
+ ];
320
+ const modelClause =
321
+ r.model && r.model !== "inherit"
322
+ ? `\n model: ${JSON.stringify(r.model)},`
323
+ : "";
324
+ lines.push(" {");
325
+ lines.push(` key: ${JSON.stringify(r.id)},`);
326
+ lines.push(` agent: ${JSON.stringify(leanAgentName(r.id))},`);
327
+ lines.push(` task: [`);
328
+ for (const part of taskParts) {
329
+ lines.push(` ${JSON.stringify(part)},`);
330
+ }
331
+ lines.push(` ].join(" "),`);
332
+ lines.push(` cwd: ${JSON.stringify(workspacePath)},`);
333
+ if (r.thinking) {
334
+ lines.push(` thinking: ${JSON.stringify(r.thinking)},`);
335
+ }
336
+ lines.push(` toolBudget: { soft: ${tbForId.soft}, hard: ${tbForId.hard} },`);
337
+ lines.push(
338
+ ` turnBudget: { maxTurns: ${budgets.turnBudget.maxTurns}, graceTurns: ${budgets.turnBudget.graceTurns} },${modelClause}`,
339
+ );
340
+ lines.push(" },");
341
+ }
342
+ lines.push(" ]);");
343
+ lines.push("}");
307
344
  lines.push("");
308
345
 
309
346
  // Gate
@@ -49,6 +49,8 @@ export interface PrepareRunInput {
49
49
  lite?: boolean;
50
50
  /** Support `--perf` performance & benchmark mode. */
51
51
  perf?: boolean;
52
+ /** Support `--full` all 6 reviewers mode. */
53
+ full?: boolean;
52
54
  /** Optional per-run gate model override. */
53
55
  gateModel?: string;
54
56
  /** Set false for dry-runs — pruning is a side effect a dry run must not have. */
@@ -150,7 +152,16 @@ export async function prepareRun(input: PrepareRunInput): Promise<PreparedRun |
150
152
  ? [{ id: "perf-review", label: "Performance Review", enabled: true, model: "inherit" }]
151
153
  : input.lite
152
154
  ? [{ id: "lite-review", label: "Lite Review", enabled: true, model: "inherit" }]
153
- : reviewersForRouting(target, config, profile);
155
+ : input.full
156
+ ? [
157
+ { id: "claude-md-compliance", label: "Claude-MD Compliance", enabled: true, model: "inherit" },
158
+ { id: "bugbot", label: "Bugbot", enabled: true, model: "inherit" },
159
+ { id: "security-review", label: "Security Review", enabled: true, model: "inherit" },
160
+ { id: "history-context", label: "History Context", enabled: true, model: "inherit" },
161
+ { id: "code-comments", label: "Code Comments", enabled: true, model: "inherit" },
162
+ { id: "perf-review", label: "Performance Review", enabled: true, model: "inherit" },
163
+ ]
164
+ : reviewersForRouting(target, config, profile);
154
165
  const skippedReasons = adaptiveSkips(profile);
155
166
  // The report tool uses this to reject findings that did not come from this
156
167
  // run's roster (stale-artifact contamination guard).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@unifan/pi-unifan-zh",
3
- "version": "1.0.10",
3
+ "version": "1.0.12",
4
4
  "description": "Pi Coding Agent 中文扩展全家桶与独立插件集",
5
5
  "type": "module",
6
6
  "main": "extensions/sessions/index.ts",