feat: initial commit - Jhonny Editor
- Adicionado estrutura completa do projeto - Configurado MCP server para Premiere Pro - Adicionado documentação e skills - Configurado Gitignore para o projeto
This commit is contained in:
@@ -0,0 +1,255 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import {
|
||||
buildEditorialContextPack,
|
||||
MAX_EDITORIAL_CONTEXT_PACK_CHARACTERS,
|
||||
MIN_EDITORIAL_CONTEXT_PACK_CHARACTERS,
|
||||
} from "../src/ai/editorial-context-pack.js";
|
||||
import {
|
||||
ProjectContextRepository,
|
||||
searchProjectContext,
|
||||
type ProjectContextDocument,
|
||||
} from "../src/context/project-context-store.js";
|
||||
import { getEditorialContextPackTools } from "../src/tools/editorial-context-pack.js";
|
||||
import { createServer } from "../src/server.js";
|
||||
import { Client, InMemoryTransport } from "@modelcontextprotocol/client";
|
||||
|
||||
function document(): ProjectContextDocument {
|
||||
return {
|
||||
schemaVersion: 1,
|
||||
projectId: "project-1",
|
||||
projectName: "Launch interview",
|
||||
revision: "context-r1",
|
||||
sourceRevision: "source-r1",
|
||||
timelineRevision: "timeline-r1",
|
||||
updatedAt: "2026-09-02T00:00:00.000Z",
|
||||
records: [
|
||||
{
|
||||
id: "source-1",
|
||||
kind: "source",
|
||||
name: "Founder interview",
|
||||
text: "Source Founder interview. Media type mov.",
|
||||
keywords: ["interview", "mov"],
|
||||
sourceId: "source-item-1",
|
||||
sourceRevision: "source-r1",
|
||||
indexedAt: "2026-09-02T00:00:00.000Z",
|
||||
},
|
||||
{
|
||||
id: "transcript-1",
|
||||
kind: "transcript",
|
||||
name: "Founder on the launch budget",
|
||||
text: "We made the launch budget work by simplifying the onboarding flow.",
|
||||
keywords: ["launch", "budget", "onboarding"],
|
||||
sequenceId: "sequence-1",
|
||||
sourceId: "source-item-1",
|
||||
startSeconds: 12.5,
|
||||
endSeconds: 17.25,
|
||||
sourceRevision: "source-r1",
|
||||
timelineRevision: "timeline-r1",
|
||||
metadata: { secret: "must-not-appear" },
|
||||
indexedAt: "2026-09-02T00:00:00.000Z",
|
||||
},
|
||||
{
|
||||
id: "shot-1",
|
||||
kind: "shot",
|
||||
name: "Product close-up",
|
||||
text: "Product close-up during the onboarding explanation.",
|
||||
keywords: ["product", "onboarding"],
|
||||
sequenceId: "sequence-1",
|
||||
sourceId: "source-item-2",
|
||||
startSeconds: 30,
|
||||
endSeconds: 35,
|
||||
sourceRevision: "source-r1",
|
||||
indexedAt: "2026-09-02T00:00:00.000Z",
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
describe("editorial context pack", () => {
|
||||
it("renders stable, compact evidence without leaking metadata", () => {
|
||||
const source = document();
|
||||
const results = searchProjectContext(source, { query: "launch budget onboarding", limit: 12 });
|
||||
const first = buildEditorialContextPack(source, { intent: "launch budget onboarding", results });
|
||||
const second = buildEditorialContextPack(source, { intent: "launch budget onboarding", results });
|
||||
|
||||
expect(first).toMatchObject({
|
||||
schemaVersion: 1,
|
||||
projectId: "project-1",
|
||||
expectedContextRevision: "context-r1",
|
||||
expectedSourceRevision: "source-r1",
|
||||
expectedTimelineRevision: "timeline-r1",
|
||||
applied: false,
|
||||
truncated: false,
|
||||
});
|
||||
expect(first.markdown).toContain("# Premiere editorial context pack");
|
||||
expect(first.markdown).toContain("evidence transcript-1");
|
||||
expect(first.markdown).toContain("12.500s–17.250s");
|
||||
expect(first.markdown).toContain("We made the launch budget work");
|
||||
expect(first.markdown).not.toContain("must-not-appear");
|
||||
expect(first.evidence[0]).toMatchObject({
|
||||
evidenceId: "transcript-1",
|
||||
kind: "transcript",
|
||||
sourceId: "source-item-1",
|
||||
startSeconds: 12.5,
|
||||
endSeconds: 17.25,
|
||||
textTruncated: false,
|
||||
});
|
||||
expect(first).toEqual(second);
|
||||
});
|
||||
|
||||
it("enforces a strict Markdown cap and labels partial evidence", () => {
|
||||
const source = document();
|
||||
source.records[1].text = `Budget evidence ${"with supporting detail ".repeat(3_000)}`;
|
||||
const results = searchProjectContext(source, { query: "budget", limit: 12 });
|
||||
const pack = buildEditorialContextPack(source, {
|
||||
intent: "budget",
|
||||
results,
|
||||
maxCharacters: MIN_EDITORIAL_CONTEXT_PACK_CHARACTERS,
|
||||
});
|
||||
|
||||
expect(pack.markdown.length).toBeLessThanOrEqual(MIN_EDITORIAL_CONTEXT_PACK_CHARACTERS);
|
||||
expect(pack.truncated).toBe(true);
|
||||
expect(pack.evidence[0]).toMatchObject({ evidenceId: "transcript-1", textTruncated: true });
|
||||
expect(pack.markdown).toContain("[excerpt truncated]");
|
||||
});
|
||||
|
||||
it("preserves the Markdown cap when project labels and revisions are long", () => {
|
||||
const source = document();
|
||||
source.projectName = "Project ".repeat(80);
|
||||
source.revision = "context-".repeat(100);
|
||||
source.sourceRevision = "source-".repeat(100);
|
||||
source.timelineRevision = "timeline-".repeat(100);
|
||||
const intent = `budget ${"review ".repeat(140)}`;
|
||||
const results = searchProjectContext(source, { query: "budget", limit: 12 });
|
||||
const pack = buildEditorialContextPack(source, {
|
||||
intent,
|
||||
results,
|
||||
maxCharacters: MIN_EDITORIAL_CONTEXT_PACK_CHARACTERS,
|
||||
});
|
||||
|
||||
expect(pack.intent).toBe(intent.trim());
|
||||
expect(pack.markdown.length).toBeLessThanOrEqual(MIN_EDITORIAL_CONTEXT_PACK_CHARACTERS);
|
||||
});
|
||||
|
||||
it("validates pack bounds before producing output", () => {
|
||||
const source = document();
|
||||
const results = searchProjectContext(source, { query: "budget" });
|
||||
expect(() => buildEditorialContextPack(source, { intent: " ", results })).toThrow("intent must not be empty");
|
||||
expect(() => buildEditorialContextPack(source, { intent: "budget", results, maxEntries: 0 })).toThrow("max_entries");
|
||||
expect(() => buildEditorialContextPack(source, { intent: "budget", results, maxCharacters: MIN_EDITORIAL_CONTEXT_PACK_CHARACTERS - 1 })).toThrow("max_characters");
|
||||
expect(() => buildEditorialContextPack(source, { intent: "budget", results, maxCharacters: MAX_EDITORIAL_CONTEXT_PACK_CHARACTERS + 1 })).toThrow("max_characters");
|
||||
});
|
||||
|
||||
it("omits unavailable record metadata from compact evidence", () => {
|
||||
const source = document();
|
||||
const record = source.records[0];
|
||||
delete record.sourceId;
|
||||
delete record.sourceRevision;
|
||||
const results = searchProjectContext(source, { query: "interview" }).filter((result) => result.record.id === record.id);
|
||||
|
||||
const pack = buildEditorialContextPack(source, { intent: "interview", results });
|
||||
|
||||
expect(pack.evidence).toHaveLength(1);
|
||||
expect(pack.evidence[0]).not.toHaveProperty("sequenceId");
|
||||
expect(pack.evidence[0]).not.toHaveProperty("sourceId");
|
||||
expect(pack.evidence[0]).not.toHaveProperty("timelineItemId");
|
||||
expect(pack.evidence[0]).not.toHaveProperty("startSeconds");
|
||||
expect(pack.evidence[0]).not.toHaveProperty("endSeconds");
|
||||
expect(pack.evidence[0]).not.toHaveProperty("sourceRevision");
|
||||
expect(pack.evidence[0]).not.toHaveProperty("timelineRevision");
|
||||
expect(pack.markdown).not.toContain("source source-item-1");
|
||||
});
|
||||
|
||||
it("reads only stored local context and rejects invalid tool input", async () => {
|
||||
const repository = new ProjectContextRepository({ backend: "memory" });
|
||||
const tools = getEditorialContextPackTools({ repository });
|
||||
await repository.put(document());
|
||||
|
||||
await expect(tools.create_editorial_context_pack.handler({
|
||||
project_id: "project-1",
|
||||
intent: "launch budget",
|
||||
kinds: ["transcript"],
|
||||
max_entries: 4,
|
||||
max_characters: 2_000,
|
||||
})).resolves.toMatchObject({
|
||||
success: true,
|
||||
data: {
|
||||
applied: false,
|
||||
evidence: [expect.objectContaining({ kind: "transcript" })],
|
||||
},
|
||||
});
|
||||
await expect(tools.create_editorial_context_pack.handler({
|
||||
project_id: "missing",
|
||||
intent: "launch budget",
|
||||
})).resolves.toMatchObject({ success: false, error: expect.stringContaining("not found") });
|
||||
await expect(tools.create_editorial_context_pack.handler({
|
||||
project_id: "project-1",
|
||||
intent: "launch budget",
|
||||
kinds: ["transcript", "transcript"],
|
||||
})).resolves.toMatchObject({ success: false, error: expect.stringContaining("duplicates") });
|
||||
await expect(tools.create_editorial_context_pack.handler({
|
||||
project_id: "project-1",
|
||||
intent: "launch budget",
|
||||
max_characters: 1,
|
||||
})).resolves.toMatchObject({ success: false, error: expect.stringContaining("max_characters") });
|
||||
await expect(tools.create_editorial_context_pack.handler({
|
||||
project_id: "p".repeat(513),
|
||||
intent: "launch budget",
|
||||
})).resolves.toMatchObject({ success: false, error: expect.stringContaining("at most 512") });
|
||||
});
|
||||
|
||||
it("rejects malformed optional filters before searching context", async () => {
|
||||
const repository = new ProjectContextRepository({ backend: "memory" });
|
||||
const tool = getEditorialContextPackTools({ repository }).create_editorial_context_pack;
|
||||
await repository.put(document());
|
||||
|
||||
await expect(tool.handler({ project_id: "project-1", intent: "budget", sequence_id: " " }))
|
||||
.resolves.toMatchObject({ success: false, error: expect.stringContaining("sequence_id") });
|
||||
await expect(tool.handler({ project_id: "project-1", intent: "budget", kinds: [] }))
|
||||
.resolves.toMatchObject({ success: false, error: expect.stringContaining("between 1 and 8") });
|
||||
await expect(tool.handler({ project_id: "project-1", intent: "budget", kinds: ["unsupported"] as any }))
|
||||
.resolves.toMatchObject({ success: false, error: expect.stringContaining("supported project-context kind") });
|
||||
await expect(tool.handler({ project_id: "project-1", intent: "budget", kinds: [1] as any }))
|
||||
.resolves.toMatchObject({ success: false, error: expect.stringContaining("supported project-context kind") });
|
||||
});
|
||||
|
||||
it("reports entry overflow and excludes records with no matched intent term", async () => {
|
||||
const repository = new ProjectContextRepository({ backend: "memory" });
|
||||
const source = document();
|
||||
for (let index = 2; index <= 100; index++) {
|
||||
source.records.push({
|
||||
...source.records[1], id: `transcript-${index}`, name: `Budget answer ${index}`,
|
||||
text: `The launch budget answer ${index}.`,
|
||||
});
|
||||
}
|
||||
await repository.put(source);
|
||||
const tool = getEditorialContextPackTools({ repository }).create_editorial_context_pack;
|
||||
|
||||
await expect(tool.handler({ project_id: "project-1", intent: "budget", max_entries: 12 }))
|
||||
.resolves.toMatchObject({
|
||||
success: true,
|
||||
data: { evidence: expect.any(Array), omittedEvidenceCount: 88, truncated: true },
|
||||
});
|
||||
await expect(tool.handler({ project_id: "project-1", intent: "unrelated", kinds: ["transcript"] }))
|
||||
.resolves.toMatchObject({ success: true, data: { evidence: [], omittedEvidenceCount: 0, truncated: false } });
|
||||
});
|
||||
|
||||
it("shares the injected server context repository with the reading tool", async () => {
|
||||
const repository = new ProjectContextRepository({ backend: "memory" });
|
||||
await repository.put(document());
|
||||
const server = createServer({}, { contextRepository: repository });
|
||||
const client = new Client({ name: "editorial-context-pack-test", version: "1.0.0" });
|
||||
const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
|
||||
await Promise.all([server.connect(serverTransport), client.connect(clientTransport)]);
|
||||
try {
|
||||
const result = await client.callTool({
|
||||
name: "create_editorial_context_pack",
|
||||
arguments: { project_id: "project-1", intent: "launch budget" },
|
||||
});
|
||||
expect((result.structuredContent as any).data).toMatchObject({ projectId: "project-1", applied: false });
|
||||
} finally {
|
||||
await client.close();
|
||||
await server.close();
|
||||
}
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user