src / projectMemory.ts

/**
 * @file projectMemory.ts
 * Shared helpers for project-scoped memory policy.
 */

import type { MemoryCategory, MemoryScope } from "./constants";

export interface ProjectMemoryConfig {
  activeProject: string;
}

export interface ProjectMemoryTargetInput {
  scope?: MemoryScope;
  project?: string | null;
  forceActiveProject?: boolean;
}

export interface ProjectMemoryTarget {
  scope: MemoryScope;
  project: string | null;
}

const PROJECT_MEMORY_CATEGORIES: MemoryCategory[] = [
  "project",
  "instruction",
  "fact",
  "note",
];

export function getDurableProjectCategories(): MemoryCategory[] {
  return [...PROJECT_MEMORY_CATEGORIES];
}

export function isDurableProjectCategory(category: MemoryCategory): boolean {
  return PROJECT_MEMORY_CATEGORIES.includes(category);
}

export function normalizeProjectSlug(project?: string | null): string | null {
  const normalized = project?.trim();
  return normalized ? normalized : null;
}

export function resolveProjectMemoryTarget(
  cfg: ProjectMemoryConfig,
  input: ProjectMemoryTargetInput,
): ProjectMemoryTarget {
  const activeProject = normalizeProjectSlug(cfg.activeProject);
  const requestedProject = normalizeProjectSlug(input.project);

  if (input.scope === "session") {
    return { scope: "session", project: null };
  }

  if (input.forceActiveProject && activeProject) {
    return { scope: "project", project: activeProject };
  }

  if (input.scope === "project") {
    return { scope: "project", project: requestedProject ?? activeProject };
  }

  if (requestedProject) {
    return { scope: "project", project: requestedProject };
  }

  return {
    scope: input.scope ?? "global",
    project: null,
  };
}

export function buildProjectMemoryTags(
  tags: string[] | undefined,
  project: string,
): string[] {
  const normalized = new Set<string>();
  for (const tag of tags ?? []) {
    const clean = tag.trim().toLowerCase();
    if (clean) normalized.add(clean);
  }
  normalized.add("project-memory");
  normalized.add(project.trim().toLowerCase());
  return [...normalized];
}