Default recurring task exports to checked

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
dotta 2026-03-23 12:42:20 -05:00
parent c41dd2e393
commit 220946b2a1
3 changed files with 105 additions and 13 deletions

View file

@ -0,0 +1,41 @@
import { describe, expect, it } from "vitest";
import { buildInitialExportCheckedFiles } from "./company-export-selection";
describe("buildInitialExportCheckedFiles", () => {
it("checks non-task files and recurring task packages by default", () => {
const checked = buildInitialExportCheckedFiles(
[
"README.md",
".paperclip.yaml",
"tasks/one-off/TASK.md",
"tasks/recurring/TASK.md",
"tasks/recurring/notes.md",
],
[
{ path: "tasks/one-off/TASK.md", recurring: false },
{ path: "tasks/recurring/TASK.md", recurring: true },
],
new Set<string>(),
);
expect(Array.from(checked).sort()).toEqual([
".paperclip.yaml",
"README.md",
"tasks/recurring/TASK.md",
"tasks/recurring/notes.md",
]);
});
it("preserves previous manual selections for one-time tasks", () => {
const checked = buildInitialExportCheckedFiles(
["README.md", "tasks/one-off/TASK.md"],
[{ path: "tasks/one-off/TASK.md", recurring: false }],
new Set(["tasks/one-off/TASK.md"]),
);
expect(Array.from(checked).sort()).toEqual([
"README.md",
"tasks/one-off/TASK.md",
]);
});
});

View file

@ -0,0 +1,56 @@
import type { CompanyPortabilityIssueManifestEntry } from "@paperclipai/shared";
function isTaskPath(filePath: string): boolean {
return /(?:^|\/)tasks\//.test(filePath);
}
function buildRecurringTaskPrefixes(
issues: Array<Pick<CompanyPortabilityIssueManifestEntry, "path" | "recurring">>,
): Set<string> {
const prefixes = new Set<string>();
for (const issue of issues) {
if (!issue.recurring) continue;
const filePath = issue.path.trim();
if (!filePath) continue;
prefixes.add(filePath);
const lastSlash = filePath.lastIndexOf("/");
if (lastSlash >= 0) {
prefixes.add(`${filePath.slice(0, lastSlash + 1)}`);
}
}
return prefixes;
}
function isRecurringTaskFile(filePath: string, recurringTaskPrefixes: Set<string>): boolean {
for (const prefix of recurringTaskPrefixes) {
if (filePath === prefix || filePath.startsWith(prefix)) return true;
}
return false;
}
export function buildInitialExportCheckedFiles(
filePaths: string[],
issues: Array<Pick<CompanyPortabilityIssueManifestEntry, "path" | "recurring">>,
previousCheckedFiles: Set<string>,
): Set<string> {
const next = new Set<string>();
const recurringTaskPrefixes = buildRecurringTaskPrefixes(issues);
for (const filePath of filePaths) {
if (previousCheckedFiles.has(filePath)) {
next.add(filePath);
continue;
}
if (!isTaskPath(filePath) || isRecurringTaskFile(filePath, recurringTaskPrefixes)) {
next.add(filePath);
}
}
return next;
}