feat(FN-2947): merge fusion/fn-2947
- Add AI-powered merge commit summarization with toggle in Settings (`packages/core/src/ai-summarize.ts`, `packages/dashboard/app/components/SettingsModal.tsx`, `packages/engine/src/merger.ts`) - Introduce `generateMergeCommitSummary` utility in `@fusion/core` that calls the configured model with a diff prompt; results appear in the commit log for merge commits - Add `aiMergeSummaryEnabled` project setting and `useAiMergeSummaryEnabled` hook in dashboard - Update `@runfusion/fusion` CLI extension to surface the new setting - Add `POST /api/tasks/:id/workflow/pre-merge` and `POST /api/tasks/:id/planning-subtask` routes (`packages/dashboard/src/routes/`) - Rework InsightsView with two-pane layout for better readability (`packages/dashboard/app/components/InsightsView.tsx`, `packages/dashboard/app/components/InsightsView.css`) - Fix `task plan` command to surface errors when planning fails (`packages/cli/src/commands/task.ts`) - Add tests for `ai-summarize`, `agent-heartbeat`, `agent-tools`, and dashboard API routes; refresh onboarding and agents-view mobile tests - Minor CSS polish: reduce AgentsView panel hover gap, improve CommitDiffTab layout, add ScriptsModal styles - Changelog and version bumps for `@runfusion/fusion` v0.9.1, CLI alias, and all workspace packages Commits merged: - feat(FN-2947): complete Step 7 — add changeset and docs deliverables - fix(FN-2947): clean unused aiSummary path - test(FN-2947): complete Step 5 — cover AI merge summary flow - feat(FN-2947): complete Step 4 — add merge summary settings toggle - feat(FN-2947): complete Step 3 — wire AI merge summaries into merger - feat(FN-2947): complete Step 2 — add merge summary setting - feat(FN-2947): complete Step 1 — add merge commit summarizer - feat(FN-2970): merge fusion/fn-2970 - feat(FN-2956): merge fusion/fn-2956 - feat(FN-2923): merge fusion/fn-2923 - feat(FN-2945): merge fusion/fn-2945 - chore(release): v0.9.1 - fix(FN-XXX): improve git manager diff layout - fix(dashboard): rework Insights view with two-pane layout - fix(FN-XXX): keep experimental views off by default Files changed: .changeset/active-agents-no-stuck-connecting.md | 13 -- .changeset/active-agents-panel-hoist-heartbeat.md | 13 -- .changeset/add-ai-merge-commit-summary.md | 5 + .changeset/fix-agent-heartbeat-terminal-links.md | 5 - .changeset/show-planning-tasks-immediately.md | 5 - CHANGELOG.md | 92 ++++++++++ docs/settings-reference.md | 3 + package.json | 2 +- packages/cli-alias/CHANGELOG.md | 15 ++ packages/cli-alias/package.json | 2 +- packages/cli/CHANGELOG.md | 12 ++ packages/cli/package.json | 2 +- packages/cli/src/__tests__/task-plan.test.ts | 1 + packages/cli/src/commands/__tests__/task.test.ts | 15 +- packages/cli/src/commands/task.ts | 15 +- packages/cli/src/extension.ts | 9 + packages/core/CHANGELOG.md | 7 + packages/core/package.json | 2 +- packages/core/src/__tests__/ai-summarize.test.ts | 64 +++++++ packages/core/src/ai-summarize.ts | 114 ++++++++++++ packages/core/src/index.ts | 3 + packages/core/src/settings-schema.ts | 1 + packages/core/src/types.ts | 4 + packages/dashboard/CHANGELOG.md | 14 ++ packages/dashboard/app/App.tsx | 28 ++- .../app/__tests__/agent-css-classes.test.ts | 3 +- packages/dashboard/app/__tests__/api.test.ts | 13 ++ packages/dashboard/app/api/legacy.ts | 2 + packages/dashboard/app/components/AgentsView.css | 22 +-- .../dashboard/app/components/CommitDiffTab.tsx | 2 +- .../dashboard/app/components/GitManagerModal.tsx | 71 ++++---- packages/dashboard/app/components/InsightsView.css | 200 ++++++++++++++++++--- packages/dashboard/app/components/InsightsView.tsx | 111 ++++++++---- .../app/components/ModelOnboardingModal.tsx | 5 +- packages/dashboard/app/components/ScriptsModal.css | 89 +++++++++ .../dashboard/app/components/SettingsModal.css | 11 +- .../dashboard/app/components/SettingsModal.tsx | 22 ++- .../dashboard/app/components/TaskDetailModal.css | 5 + packages/dashboard/app/components/TodoView.tsx | 2 + .../app/components/__tests__/App.test.tsx | 33 ++++ .../app/components/__tests__/InsightsView.test.tsx | 25 ++- .../__tests__/ModelOnboardingModal.test.tsx | 4 +- .../app/components/__tests__/QuickChatFAB.test.tsx | 14 +- .../__tests__/SettingsModalNodeRouting.test.tsx | 12 +- .../app/components/__tests__/TodoView.test.tsx | 4 +- .../__tests__/agents-view-mobile.test.tsx | 5 +- .../components/__tests__/onboarding-flow.test.tsx | 2 +- .../app/hooks/__tests__/useAppSettings.test.ts | 1 + .../app/hooks/__tests__/useTaskHandlers.test.ts | 4 +- packages/dashboard/app/hooks/useAppSettings.ts | 12 ++ packages/dashboard/app/hooks/useTaskHandlers.ts | 4 +- packages/dashboard/package.json | 2 +- packages/dashboard/src/__tests__/routes.test.ts | 14 ++ packages/dashboard/src/routes.ts | 4 + .../dashboard/src/routes/register-git-github.ts | 12 ++ .../src/routes/register-planning-subtask-routes.ts | 3 + .../src/routes/register-task-workflow-routes.ts | 7 + packages/desktop/CHANGELOG.md | 7 + packages/desktop/package.json | 2 +- packages/engine/CHANGELOG.md | 11 ++ packages/engine/package.json | 2 +- .../engine/src/__tests__/agent-heartbeat.test.ts | 10 ++ .../src/__tests__/agent-tools-delegation.test.ts | 2 + packages/engine/src/__tests__/agent-tools.test.ts | 37 ++++ packages/engine/src/__tests__/cron-runner.test.ts | 4 + packages/engine/src/__tests__/merger.test.ts | 75 ++++++++ .../src/__tests__/node-routing-policy.test.ts | 25 ++- .../src/__tests__/pr-comment-handler.test.ts | 8 + .../src/__tests__/scheduler-node-routing.test.ts | 18 +- packages/engine/src/__tests__/triage.test.ts | 2 + packages/engine/src/agent-heartbeat.ts | 13 +- packages/engine/src/agent-tools.ts | 13 +- packages/engine/src/cron-runner.ts | 7 +- packages/engine/src/executor.ts | 2 +- packages/engine/src/merger.ts | 181 ++++++++----------- packages/engine/src/mission-execution-loop.ts | 8 + packages/engine/src/pr-comment-handler.ts | 5 + packages/engine/src/project-engine.ts | 8 + packages/engine/src/routine-runner.ts | 4 + packages/engine/src/scheduler.ts | 8 +- packages/engine/src/triage.ts | 4 + packages/mobile/CHANGELOG.md | 7 + packages/mobile/package.json | 2 +- packages/pi-claude-cli/CHANGELOG.md | 7 + packages/pi-claude-cli/package.json | 2 +- packages/plugin-sdk/CHANGELOG.md | 10 ++ packages/plugin-sdk/package.json | 2 +- .../examples/fusion-plugin-auto-label/CHANGELOG.md | 8 + .../examples/fusion-plugin-auto-label/package.json | 2 +- .../examples/fusion-plugin-ci-status/CHANGELOG.md | 8 + .../examples/fusion-plugin-ci-status/package.json | 2 +- .../fusion-plugin-notification/CHANGELOG.md | 8 + .../fusion-plugin-notification/package.json | 2 +- .../fusion-plugin-settings-demo/CHANGELOG.md | 8 + .../fusion-plugin-settings-demo/package.json | 2 +- plugins/fusion-plugin-hermes-runtime/CHANGELOG.md | 8 + plugins/fusion-plugin-hermes-runtime/package.json | 2 +- .../fusion-plugin-openclaw-runtime/CHANGELOG.md | 8 + .../fusion-plugin-openclaw-runtime/package.json | 2 +- .../fusion-plugin-paperclip-runtime/CHANGELOG.md | 8 + .../fusion-plugin-paperclip-runtime/package.json | 2 +- 101 files changed, 1388 insertions(+), 334 deletions(-) Fusion-Task-Id: FN-2947
This commit is contained in:
@@ -1,6 +1,16 @@
|
||||
import { describe, it, expect, beforeEach, vi } from "vitest";
|
||||
|
||||
const { getFnAgentMock } = vi.hoisted(() => ({
|
||||
getFnAgentMock: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock("../ai-engine-loader.js", () => ({
|
||||
getFnAgent: getFnAgentMock,
|
||||
}));
|
||||
|
||||
import {
|
||||
summarizeTitle,
|
||||
summarizeMergeCommit,
|
||||
summarizeCommitBody,
|
||||
sanitizeCommitSubject,
|
||||
MAX_COMMIT_SUBJECT_LENGTH,
|
||||
@@ -8,10 +18,12 @@ import {
|
||||
getRateLimitResetTime,
|
||||
validateDescription,
|
||||
SUMMARIZE_SYSTEM_PROMPT,
|
||||
MERGE_COMMIT_SUMMARIZE_SYSTEM_PROMPT,
|
||||
COMMIT_BODY_SYSTEM_PROMPT,
|
||||
MAX_DESCRIPTION_LENGTH,
|
||||
MIN_DESCRIPTION_LENGTH,
|
||||
MAX_TITLE_LENGTH,
|
||||
MAX_MERGE_COMMIT_SUMMARY_LENGTH,
|
||||
MAX_COMMIT_BODY_INPUT_LENGTH,
|
||||
MAX_COMMIT_BODY_LENGTH,
|
||||
DEFAULT_COMMIT_BODY_TIMEOUT_MS,
|
||||
@@ -25,6 +37,8 @@ import {
|
||||
describe("ai-summarize", () => {
|
||||
beforeEach(() => {
|
||||
__resetSummarizeState();
|
||||
getFnAgentMock.mockReset();
|
||||
getFnAgentMock.mockResolvedValue(null);
|
||||
});
|
||||
|
||||
// ── Constants ──────────────────────────────────────────────────────────────
|
||||
@@ -169,6 +183,56 @@ describe("ai-summarize", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("summarizeMergeCommit", () => {
|
||||
it("returns null when commit log and diff stat are empty", async () => {
|
||||
expect(await summarizeMergeCommit("", "", "/tmp")).toBeNull();
|
||||
expect(await summarizeMergeCommit(" ", "\n\n", "/tmp")).toBeNull();
|
||||
});
|
||||
|
||||
it("returns summary text when AI responds", async () => {
|
||||
const prompt = vi.fn().mockResolvedValue(undefined);
|
||||
getFnAgentMock.mockResolvedValue(() =>
|
||||
Promise.resolve({
|
||||
session: {
|
||||
prompt,
|
||||
dispose: vi.fn(),
|
||||
state: {
|
||||
messages: [
|
||||
{
|
||||
role: "assistant",
|
||||
content: "Updated merger and settings wiring for AI commit summaries.",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
const summary = await summarizeMergeCommit(
|
||||
"- feat: add summary\n- test: add coverage",
|
||||
"merger.ts | 20 ++++++++++-----",
|
||||
"/tmp"
|
||||
);
|
||||
|
||||
expect(summary).toBe("Updated merger and settings wiring for AI commit summaries.");
|
||||
expect(prompt).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("throws AiServiceError when AI engine is unavailable", async () => {
|
||||
await expect(
|
||||
summarizeMergeCommit("- feat: add summary", "merger.ts | 2 ++", "/tmp")
|
||||
).rejects.toThrow(AiServiceError);
|
||||
await expect(
|
||||
summarizeMergeCommit("- feat: add summary", "merger.ts | 2 ++", "/tmp")
|
||||
).rejects.toThrow("AI engine not available");
|
||||
});
|
||||
|
||||
it("exposes merge summary constants", () => {
|
||||
expect(MERGE_COMMIT_SUMMARIZE_SYSTEM_PROMPT).toContain("1-3 concise sentences");
|
||||
expect(MAX_MERGE_COMMIT_SUMMARY_LENGTH).toBe(300);
|
||||
});
|
||||
});
|
||||
|
||||
describe("summarizeCommitBody", () => {
|
||||
it("returns null for empty diff stat (nothing to summarize)", async () => {
|
||||
expect(await summarizeCommitBody("", "/tmp")).toBeNull();
|
||||
|
||||
Reference in New Issue
Block a user