UNPKG

@phren/cli

Version:

Knowledge layer for AI agents - CLI, MCP server, and data layer

198 lines (197 loc) • 9.32 kB
import { type PhrenResult } from "../shared.js"; import { type ProjectConfigOverrides } from "../project-config.js"; /** @internal Exported for tests. */ export declare const MAX_QUEUE_ENTRY_LENGTH = 500; export interface RetentionPolicy { schemaVersion?: number; ttlDays: number; retentionDays: number; autoAcceptThreshold: number; minInjectConfidence: number; decay: { d30: number; d60: number; d90: number; d120: number; }; } export interface WorkflowPolicy { schemaVersion?: number; lowConfidenceThreshold: number; riskySections: Array<"Review" | "Stale" | "Conflicts">; taskMode: "off" | "manual" | "suggest" | "auto"; findingSensitivity: "minimal" | "conservative" | "balanced" | "aggressive"; } export interface IndexPolicy { schemaVersion?: number; includeGlobs: string[]; excludeGlobs: string[]; includeHidden: boolean; } /** * Outcome of the push leg of a sync. * * `saved-local` means "committed, nothing to push or no remote" — a success. * It used to double as the report for a *failed* push, which is how a store * could go two months without syncing while every Stop hook looked healthy. * Failures now get their own values: * * - `pull-failed` - the fetch/merge leg failed, so push never ran * - `push-failed` — push was attempted and rejected * - `unrelated-histories` — local and remote share no merge base; no amount of * retrying will fix it, so it is called out by name */ export type PushStatus = "saved-local" | "saved-pushed" | "no-upstream" | "pull-failed" | "push-failed" | "unrelated-histories" | "error"; export declare const PUSH_STATUSES: readonly PushStatus[]; /** Push statuses that mean the store is NOT in sync with its remote. */ export declare const FAILED_PUSH_STATUSES: ReadonlySet<PushStatus>; export type AutoSaveStatus = "clean" | "saved-local" | "saved-pushed" | "no-upstream" | "sync-failed" | "error"; export declare const AUTO_SAVE_STATUSES: readonly AutoSaveStatus[]; export interface RuntimeHealth { schemaVersion?: number; lastSessionStartAt?: string; lastPromptAt?: string; lastStopAt?: string; lastAutoSave?: { at: string; status: AutoSaveStatus; detail?: string; }; lastGovernance?: { at: string; status: "ok" | "error"; detail: string; }; lastSync?: { lastPullAt?: string; lastPullStatus?: "ok" | "error"; lastPullDetail?: string; lastSuccessfulPullAt?: string; lastPushAt?: string; lastPushStatus?: PushStatus; lastPushDetail?: string; unsyncedCommits?: number; /** * Consecutive sync attempts that failed to reach the remote. Reset to 0 on * any successful push. This is what turns "one flaky push" into "this store * has been broken for weeks" without needing to keep a history. */ consecutiveFailures?: number; /** Last time a push actually reached the remote. */ lastSuccessfulPushAt?: string; /** Commits ahead of and behind the upstream tracking ref at the last sync outcome. */ ahead?: number; behind?: number; /** The last failure of a store sync in plain words, cleared by the next full success. */ lastError?: string; lastErrorAt?: string; }; } export type SyncStatus = NonNullable<RuntimeHealth["lastSync"]>; export interface BuildSyncStatusOpts { now: string; pushStatus: SyncStatus["lastPushStatus"]; pushDetail?: string; pullAt?: string; pullStatus?: SyncStatus["lastPullStatus"]; pullDetail?: string; successfulPullAt?: string; unsyncedCommits?: number; consecutiveFailures?: number; successfulPushAt?: string; } export declare function buildSyncStatus(opts: BuildSyncStatusOpts): SyncStatus; /** Warn after this many consecutive failed syncs. */ export declare const SYNC_FAILURE_WARN_RUNS = 3; /** ...or after this many days without a successful push, whichever comes first. */ export declare const SYNC_FAILURE_WARN_DAYS = 3; export interface SyncOutageAssessment { /** True when the store has been failing long enough to tell the user. */ degraded: boolean; consecutiveFailures: number; daysSinceSuccess: number | null; /** One-line, user-facing explanation. Empty when not degraded. */ summary: string; } /** * Decide whether a store's sync failures have gone on long enough to warrant a * visible warning. Pure so both the Stop hook and `phren status` can ask the * same question and get the same answer. */ export declare function assessSyncOutage(sync: SyncStatus | undefined, nowMs?: number): SyncOutageAssessment; export type RetentionPolicyPatch = Partial<Omit<RetentionPolicy, "decay">> & { decay?: Partial<RetentionPolicy["decay"]>; }; export declare const GOVERNANCE_SCHEMA_VERSION = 1; /** Default retention policy. Exported so {@link config/schema} can render one source of truth. */ export declare const DEFAULT_POLICY: RetentionPolicy; /** Default workflow policy. Exported so {@link config/schema} can render one source of truth. */ export declare const DEFAULT_WORKFLOW_POLICY: WorkflowPolicy; /** Default index policy. Exported so {@link config/schema} can render one source of truth. */ export declare const DEFAULT_INDEX_POLICY: IndexPolicy; type GovernanceSchema = "retention-policy" | "workflow-policy" | "index-policy"; export declare function validateGovernanceJson(filePath: string, schema: GovernanceSchema): boolean; export interface ResolvedConfig { findingSensitivity: WorkflowPolicy["findingSensitivity"]; proactivity: { base?: "high" | "medium" | "low"; findings?: "high" | "medium" | "low"; tasks?: "high" | "medium" | "low"; }; taskMode: WorkflowPolicy["taskMode"]; retentionPolicy: RetentionPolicy; workflowPolicy: WorkflowPolicy; } export { VALID_PROACTIVITY_LEVELS, VALID_TASK_MODES, type TaskMode, VALID_FINDING_SENSITIVITY, type FindingSensitivityLevel, VALID_RISKY_SECTIONS, } from "./policy-constants.js"; export declare function getProjectConfigOverrides(phrenPath: string, projectName: string): ProjectConfigOverrides | null; export declare function mergeConfig(phrenPath: string, projectName?: string, profile?: string): ResolvedConfig; export declare function getRetentionPolicy(phrenPath: string, projectName?: string): RetentionPolicy; export declare function updateRetentionPolicy(phrenPath: string, patch: RetentionPolicyPatch): PhrenResult<RetentionPolicy>; export declare function getWorkflowPolicy(phrenPath: string, projectName?: string): WorkflowPolicy; export declare function updateWorkflowPolicy(phrenPath: string, patch: Partial<WorkflowPolicy>): PhrenResult<WorkflowPolicy>; export declare function getIndexPolicy(phrenPath: string): IndexPolicy; export declare function updateIndexPolicy(phrenPath: string, patch: Partial<IndexPolicy>): PhrenResult<IndexPolicy>; export declare function getRuntimeHealth(phrenPath: string): RuntimeHealth; export declare function updateRuntimeHealth(phrenPath: string, patch: Partial<RuntimeHealth>): RuntimeHealth; export declare function normalizeQueueEntryText(raw: string, opts?: { truncate?: boolean; }): PhrenResult<{ text: string; truncated: boolean; }>; /** * A queue entry that carries structured provenance alongside its text. * * `meta` is a pre-rendered run of HTML comments (e.g. `<!-- source:extract --> * <!-- phren:cite {...} -->`) appended verbatim to the queue line. It exists so a * queued candidate stays *promotable*: `approveQueueItem` reads it back and hands * the same repo/commit/source provenance to the normal add-finding path, which * makes a promoted finding indistinguishable from one added directly. * * Producers build the comments themselves (this module deliberately does not import * the citation helpers, to keep governance free of a content-layer import cycle). */ export interface ReviewQueueEntry { text: string; meta?: string; } export type ReviewQueueEntryInput = string | ReviewQueueEntry; export declare function appendReviewQueue(phrenPath: string, project: string, section: "Review" | "Stale" | "Conflicts", entries: ReviewQueueEntryInput[]): PhrenResult<number>; export interface PruneMemoriesResult { /** Human-readable summary (plus dry-run detail lines), ready to print. */ message: string; /** Entries deleted for exceeding the retention window. */ pruned: number; /** TTL-expired entries promoted into review.md's `## Stale` section. */ ttlExpired: number; } /** * Delete entries past the retention window and promote TTL-expired entries into review.md. * * TTL promotion lives here rather than in the CLI handler so that nightly maintenance * (`handleBackgroundMaintenance`, which calls this directly) gets it too — previously it * only ran on a manual `phren maintain prune`, leaving `## Stale` empty while `## Review` * filled up. */ export declare function pruneDeadMemories(phrenPath: string, project?: string, dryRun?: boolean): PhrenResult<PruneMemoriesResult>; export declare function consolidateProjectFindings(phrenPath: string, project: string, dryRun?: boolean): PhrenResult<string>;