/** * history command — the artifacts-store front door: the read-only * inventory (save-artifacts plan, Ticket 5) widened by the * history-journal merge with `note` (T4), `recall` (T5), and `clear` * (T6a — history's first MUTATING op). * * Subcommands over `/index.json` and the master reports it * references: * * - `history list [--since N] [--limit N] [--command C]` — newest * first, from the LOG ONLY (a master file with no log entry is an * orphan and invisible, DESIGN D5). * - `history show ` — the join: the log entry plus the * master report content, keyed by requestId. Unknown id and a live * entry whose master vanished are FILE_ERROR (D7/D8). * - `history stats` — counts by command / artifactFormat / kind, * summed master bytes, and the oldest/newest span. * - `history note` / `history recall` — see their own help. * - `history clear [--all]` — the valve: journal kind by default, * full wipe under --all (rewrites the log under the write lock). * * Like `usage` (DESIGN D8), this module is presentation + aggregation * only: I/O happens through injectable readers so every path is * hermetically testable, and reads are fail-open — a missing store is * the normal empty case, a corrupt log degrades to empty plus a notice. * The command is credential-free (no Provider resolution, no Adapter, * no transport) and dispatched before the credentialed config load. */ import type { CommandResult } from "../command-invocation.js"; import type { ArtifactsLog, LogEntryKind, SaveLogEntry } from "../lib/artifacts.js"; import type { JournalableCapability } from "../lib/journal.js"; /** One list row: the inventory view of a log entry. */ export interface HistoryEntrySummary { readonly requestId?: string; readonly timestamp: number; /** Save entries: the command; journal entries: the capability (T2a review nit — journal rows have no command). */ readonly command: string; readonly provider: SaveLogEntry["provider"]; /** Save entries only; journal entries render "-" (no master, no format). */ readonly artifactFormat: string; readonly kind: SaveLogEntry["kind"]; readonly exportPath?: string; /** T6b `--repeats` rows only: the full entry a repeat marker repeats. */ readonly repeatOf?: string; } /** `history list` data-mode envelope. */ export interface HistoryListReport { readonly schemaVersion: 1; readonly generatedAt: number; /** Matches after filtering, before --limit slicing. */ readonly total: number; readonly entries: readonly HistoryEntrySummary[]; } /** `history show` data-mode envelope: the join by requestId. */ export interface HistoryShowReport { readonly schemaVersion: 1; readonly entry: SaveLogEntry; /** Parsed report envelope for json masters; `{ markdown }` for md masters. */ readonly report: unknown; } /** `history stats` data-mode envelope. */ export interface HistoryStatsReport { readonly schemaVersion: 1; readonly generatedAt: number; readonly total: number; readonly byCommand: Readonly>; readonly byArtifactFormat: Readonly>; readonly byKind: Readonly>; /** Sum of on-disk master sizes over logged entries (missing files add 0). */ readonly masterBytes: number; readonly oldest?: number; readonly newest?: number; /** * T6b (DESIGN D5): the journal kind split into full skeleton entries * vs repeat markers. Absent when the store holds no journal rows; * the parts sum to `byKind.journal`. */ readonly journalSplit?: { readonly full: number; readonly marker: number; }; } export interface HistoryListOptions { readonly sinceDays?: number; readonly limit?: number; readonly command?: string; /** T6b: filter to one entry kind. */ readonly kind?: LogEntryKind; /** T6b: include repeat-marker rows (skipped by default — D5 ruling). */ readonly repeats?: boolean; readonly now: () => number; } /** * Fold the log into the list report: optional `--command` filter, a * `--since N` UTC-day window inclusive of today (the `usage --days` * semantics: the window's lower edge is UTC midnight `N-1` whole days * back, so every entry of today and the previous `N-1` days is kept — * a rolling `now - N*DAY` cutoff would silently drop same-day entries * and make `--since 1` effectively empty; cold-review round 1 * finding 2), newest-first ordering (timestamp desc, requestId desc on * ties), then `--limit` slicing. `total` counts post-filter, pre-slice. * Pure. */ export declare function buildHistoryListReport(log: ArtifactsLog, options: HistoryListOptions): HistoryListReport; /** Master-content reader: returns the file text, or undefined when missing. */ export type ReadMaster = (entry: SaveLogEntry) => Promise; /** * The join: find the entry by requestId, read its master, and surface * `{ entry, report }`. Unknown ids are FILE_ERROR; a live entry whose * master is gone is FILE_ERROR naming the master path; a corrupt json * master is FILE_ERROR rather than a crash. Markdown masters surface as * `{ markdown }` (they are not JSON). */ export declare function buildHistoryShowReport(log: ArtifactsLog, requestId: string, readMaster: ReadMaster): Promise; /** Master-size reader (bytes); missing files should resolve 0, not throw. */ export type MasterSizeOf = (entry: SaveLogEntry) => Promise; /** Aggregate counts, summed master bytes, and the timestamp span. Pure except the size reader. */ export declare function buildHistoryStatsReport(log: ArtifactsLog, masterSizeOf: MasterSizeOf, now: () => number): Promise; export interface HistoryCommandDependencies { readonly subcommand: "list" | "show" | "stats"; readonly readLog: () => Promise<{ log: ArtifactsLog; notice?: string; }>; readonly readMaster: ReadMaster; readonly masterSizeOf: MasterSizeOf; readonly notice: (message: string) => void; readonly now: () => number; readonly sinceDays?: number; readonly limit?: number; readonly command?: string; /** T6b: filter to one entry kind. */ readonly kind?: LogEntryKind; /** T6b: include repeat-marker rows (skipped by default — D5 ruling). */ readonly repeats?: boolean; readonly requestId?: string; } /** * Run one `history` subcommand: fail-open log read (a read notice is * flushed through the invocation seam's stderr channel), then the pure * aggregation above, returned as base data with the shared text-mode * presentation. Exit 0; the FILE_ERROR paths throw and ride the seam's * existing error boundary. */ export declare function historyCommand(deps: HistoryCommandDependencies): Promise; /** `history clear` data-mode envelope. */ export interface HistoryClearReport { readonly schemaVersion: 1; readonly generatedAt: number; /** "journal" (bare) or "all" (--all). */ readonly scope: "journal" | "all"; /** Total entries removed. */ readonly removed: number; /** Removed counts by entry kind. */ readonly removedByKind: Readonly>; /** Entries kept (0 under --all). */ readonly kept: number; /** Save masters deleted under --all (0 bare). */ readonly mastersDeleted: number; } /** * `history clear` (PRD AC7): bare = the journal-kind valve — every * kind:"journal" entry (full entries AND repeat markers) is rewritten * away; save entries and their masters are byte-untouched. `--all` is * the full wipe: save entries go too and their master files are * deleted. The rewrite runs inside the artifacts write lock * (`clearArtifactsLog`); a corrupt pre-state reads fail-open EMPTY, so * clear succeeds and writes back a valid empty log. Journaling itself * is untouched — the next search/read/research re-populates (the * response cache is never touched; that is \`cache clear\`). */ export declare function historyClearCommand(input: { readonly dir: string; readonly all: boolean; readonly now: () => number; readonly notice: (message: string) => void; readonly lock?: { readonly timeoutMs?: number; readonly setTimeout?: typeof setTimeout; }; }): Promise; /** One dossier section's identity set (derived from a full journal entry). */ export interface HistoryExportSection { readonly requestId: string; readonly timestamp: number; readonly capability: JournalableCapability; readonly query: string; /** Rendered per family conventions (incl. the `x (cache)` qualifier). */ readonly provider: string; readonly contentHash: string; readonly rows: readonly { readonly url: string; readonly title: string; }[]; readonly tags: readonly string[]; readonly saveRef?: string; /** * Verdict on the saveRef'd master (review batch 3): the master's body * text when read from disk; false when absent/unreadable; absent when * the save entry itself is not in the log. */ readonly saveMaster?: string | boolean; } /** `history export` data-mode envelope. */ export interface HistoryExportReport { readonly schemaVersion: 1; readonly generatedAt: number; /** The parsed `--since` lower bound, when given. */ readonly since?: number; /** Sections rendered — FULL entries only (repeat markers never). */ readonly total: number; /** The deterministic markdown dossier (byte-identical for the same log). */ readonly markdown: string; /** Section requestIds, newest-first, post-filter (the refs basis). */ readonly sectionsOrder: readonly string[]; } /** * Seam for the export dossier's saveRef resolution (review batch 3): * the master's body text when the saveRef'd --save master was read from * disk; `false` when absent/unreadable; `undefined` when the save entry * itself is not in the log. Body inclusion is a LOCAL read of the file * the log already points at — never a re-fetch (no network, no cache). */ export type MasterExists = (requestId: string) => Promise; /** * Render the export dossier (T6c, PRD AC5): pure markdown over the * filtered FULL journal entries — one section per finding (entry * identity from the skeleton), one provenance line * `{url, at, contentHash}` per skeleton row (`at` = entry timestamp * ISO, `contentHash` = the ENTRY's hash). Repeat markers are NEVER * sections (the list default-skip ruling, DESIGN D5); save entries are * not findings either — a save surfaces as its skeleton's `saveRef` * pointer, and the saveRef'd master's body inlines when present (read * from disk only — never re-fetched: no network, no response-cache * reads). Sections order newest-first (timestamp desc, requestId desc) * — derived from entry fields, not append order, so the same set * renders byte-identically regardless of append sequence. */ export declare function buildHistoryExportReport(log: ArtifactsLog, options: { readonly since?: number; readonly now: () => number; readonly masterExists?: MasterExists; }): Promise; /** * T6c: the export command. Read-only over the network: `readLog` and a * LOCAL read of saveRef'd --save masters are the ONLY I/O — zero * network, zero cache reads, no re-fetch ever (the master's body is the * durable local copy the log already points at). Fail-open on a missing * store (header-only dossier, exit 0); a corrupt log's read-notice * rides the stderr notice seam. */ export declare function historyExportCommand(input: { readonly readLog: () => Promise; readonly masterExists?: MasterExists; readonly notice: (message: string) => void; readonly now: () => number; readonly since?: number; }): Promise; export declare const HISTORY_HELP = "History - Saved --save artifacts + research journal (list / show /\nstats / note / recall / export; clear MUTATES)\n\nUsage:\n scoutline history list [--since N] [--limit N] [--command ]\n [--kind ] [--repeats]\n scoutline history show \n scoutline history stats\n scoutline history note --capability \n [--url [--title ]]... [--tags a,b]\n scoutline history recall <text> [--limit N] [--as-of <date>]\n [--capability <search|read|research|science>]\n scoutline history export [--since <date>]\n scoutline history clear [--all]\n\nReads the artifact store (default ~/.scoutline/artifacts/, override with\nSCOUTLINE_ARTIFACTS_DIR) without touching Providers, credentials, or the\nresponse cache. Every subcommand except clear is read-only: reads fail\nopen \u2014 a missing store is an empty listing (exit 0); a corrupt log is\nignored with a stderr notice.\n\nOptions:\n list Saved runs, newest first. --since N keeps the last N UTC days\n (today inclusive); --limit N slices the newest N; --command\n filters by command name; --kind narrows to save or journal\n entries; --repeats also lists warm-repeat markers (skipped\n by default \u2014 markers render as their own row naming the\n entry they repeat). The table's kind column marks each row.\n show One saved run: the metadata record joined with the report\n content by requestId.\n stats Counts by command, artifact format, and entry kind, plus the\n total master bytes and oldest/newest span. Journal rows also\n split into full entries vs repeat markers (journal: N full,\n M marker). Note: journal rows have no command, so the\n commands/ fold counts them under their capability\n (search/read/research) \u2014 the key set mixes commands and\n capabilities.\n note Write an explicit journal entry: hand-supplied work record or\n observation (see `scoutline history note --help`). Not\n suppressed by config \"journal\": false \u2014 that switch governs\n the always-on recording, and note is opt-in by construction.\n recall Re-find past research from journal skeletons: lexical token\n overlap over recorded queries + skeletons, ranked by score\n then recency (see `scoutline history recall --help`). No\n network, no cache reads, no master files \u2014 pure log scoring.\n export Markdown dossier of journal findings, newest first: one\n section per full entry with a {url, at, contentHash}\n provenance line per skeleton row; a saveRef'd --save\n master's body is inlined when present (see `scoutline\n history export --help`).\n clear The valve (MUTATES the store; see `scoutline history clear\n --help`): bare clear removes the journal kind only \u2014 the\n fast-refilling layer. --save artifacts need `--all`.\n\nExit codes:\n 0 Success (including the empty fail-open cases)\n 1 Unknown requestId or missing master (FILE_ERROR); invalid flags\n (VALIDATION_ERROR)\n\nExamples:\n scoutline search \"rust vs go\" --save report.json\n scoutline history list --limit 5\n scoutline history list --kind journal --repeats\n scoutline history show 20260829T142233Z-7f3a\n scoutline history stats\n scoutline history note --capability search \"compared rust vs go\" \\\n --url https://go.dev/doc --title \"Go Documentation\" --tags lang-comparison\n scoutline history clear\n scoutline history clear --all\n"; export declare const HISTORY_NOTE_HELP = "History note - Write an explicit journal entry\n\nUsage:\n scoutline history note --capability <search|read|research|science> <text>\n [--url <url> [--title <title>]]... [--tags a,b,c]\n\nRecords hand-written work or observations into the research journal \u2014\nthe re-homed `journal record`: the same kind:\"journal\" entry the\nalways-on recording writes, but supplied by you rather than a Provider\nrun. Notes are local-only, redacted at the write seam, 0600, log-only\n(no master file), and never re-fetched. The entry's provider field is\nthe sentinel \"note\": no Provider served it, and the routing is not\nhand-choosable. Notes ignore the always-on escape hatches \u2014 config\n\"journal\": false does NOT suppress an explicit note (that switch\ngoverns automatic recording; note is opt-in by construction).\n\nOptions:\n --capability <search|read|research|science>\n The capability the note records (required). Drives the\n skeleton shape: search = url+title list; read = exactly one\n {url,title} row; research = citations list.\n <text> The note itself: the query (search) or URL (read/research)\n plus any observation text (required, positional).\n --url <url>\n One skeleton row. Repeat for multi-row skeletons (search,\n research); a read note takes EXACTLY one. Without --url the\n skeleton is an empty list (a bare observation; invalid for\n read).\n --title <title>\n Title for the preceding --url row; defaults to the url\n itself. Belongs to the nearest preceding --url.\n --tags <a,b,c>\n Comma-separated tags stored on the entry.\n\nExit codes:\n 0 Note recorded\n 1 Missing/invalid --capability, missing text, a valueless --url or\n --title, a read note without exactly one --url (VALIDATION_ERROR)\n\nExamples:\n scoutline history note --capability search \"compared rust vs go\" \\\n --url https://go.dev/doc --title \"Go Documentation\" --tags lang-comparison\n scoutline history note --capability read \"read the announcement\" \\\n --url https://example.com/changelog\n scoutline history note --capability research \"open question on quotas\"\n"; export declare const HISTORY_RECALL_HELP = "History recall - Re-find past research from the journal skeletons\n\nUsage:\n scoutline history recall <text> [--limit N] [--as-of <date>] \\\n [--capability <search|read|research|science>]\n\nLexical recollection over the recorded journal corpus: token overlap\nbetween your recall text and each journal entry's query + skeleton\ntext (titles/urls/citations), ranked score DESC then recency DESC.\nRepeat markers never appear as separate results - they resolve to the\nentry they repeat and advance its lastAsked. Saved --save artifacts\nare never text-searched (their args are flags-only); a save surfaces\nonly through its cross-linked skeleton (saveRef). No network, no\nresponse-cache reads, no master files are ever opened - recall is\npure scoring over the log (no re-fetch, ever).\n\nOptions:\n <text> The recall text (required, positional).\n --limit N Keep the top N ranked results.\n --as-of <date> Temporal boundary: entries with timestamp\n <= date. Accepts ISO-8601 or epoch-ms.\n --capability <name> Restrict the corpus to one capability.\n\nAn empty or missing journal recalls an empty list (exit 0) with one\nstderr orientation line.\n\nExit codes:\n 0 Success (including the empty fail-open cases)\n 1 Missing text; invalid --limit/--as-of/--capability values\n (VALIDATION_ERROR)\n\nExamples:\n scoutline history recall \"rust vs go\"\n scoutline history recall \"quota design\" --capability research --limit 5\n scoutline history recall \"mcp transport\" --as-of 2026-09-01T00:00:00Z\n"; /** T5 (`history recall`, DESIGN D4): the recall command. Pure scoring * over the read log — `readLog` is the only I/O (fail-open, missing * store = empty). Zero network, zero cache reads, masters never * opened. The empty-JOURNAL case emits ONE orientation notice (the * existing notice seam — stderr, stdout stays data-only). */ export declare function historyRecallCommand(input: { readonly readLog: () => Promise<import("../lib/artifacts.js").ReadLogResult>; readonly notice: (message: string) => void; readonly text: string; readonly asOf?: number; readonly capability?: JournalableCapability; readonly limit?: number; }): Promise<CommandResult>; export declare const HISTORY_CLEAR_HELP = "History clear - Clear the research journal (the valve; MUTATES the store)\n\nUsage:\n scoutline history clear [--all]\n\nBare `history clear` removes the JOURNAL kind only: every\nkind:\"journal\" entry \u2014 full skeleton entries AND repeat markers \u2014 is\nrewritten away under the artifacts write lock. --save artifacts and\ntheir report files are untouched. The journal is the fast-refilling\nlayer: it repopulates as you run search/read/research (journaling is\nnever disabled by clearing).\n\n--all extends the wipe: --save entries go too AND their master files\nare deleted. This is the full wipe; nothing survives it.\n\nA corrupt or unrecognized log reads fail-open EMPTY, so clear succeeds\nand writes back a valid empty log. The response cache is NOT touched \u2014\nuse `scoutline cache clear` for that.\n\nOptions:\n --all Full wipe: remove save entries too and delete their master\n files (default: journal kind only).\n\nExit codes:\n 0 Success (including clearing an empty or corrupt store)\n 1 Invalid flags (VALIDATION_ERROR); lock timeout (LOCK_TIMEOUT)\n\nExamples:\n scoutline history clear\n scoutline history clear --all\n"; export declare const HISTORY_EXPORT_HELP = "History export - Markdown dossier of journal findings (read-only)\n\nUsage:\n scoutline history export [--since <date>]\n\nRender a deterministic markdown dossier over the journal: one section\nper finding (full journal entries only \u2014 search/read/research\nskeletons), newest first. Each section carries the entry identity\n(query, capability, provider, recorded time, requestId, tags) and a\nprovenance line `{url, at, contentHash}` per skeleton row \u2014 the cited\nidentity of every source, never its content. Repeat markers are never\nsections (same ruling as list default). A cross-linked --save artifact\nappears as its saveRef pointer, annotated by existence, and the\nsaveRef'd master's body inlines when present \u2014 read from disk only,\nnever re-fetched (no network, no response-cache reads). Same log\nrenders byte-identical output.\n\nOptions:\n --since <date> Lower bound on entry timestamps (inclusive: >=).\n Accepts ISO-8601 or epoch-ms.\n\nExit codes:\n 0 Success (including an empty or missing store \u2014 header-only\n dossier)\n 1 Invalid --since value or unexpected arguments (VALIDATION_ERROR)\n\nExamples:\n scoutline history export\n scoutline history export --since 2026-09-01\n"; //# sourceMappingURL=history.d.ts.map