@vortex-os/base 0.0.1 → 0.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -1,24 +1,2970 @@
1
+ import { vector, recall, sessionArchive } from '@vortex-os/memory-extended';
2
+
1
3
  /**
2
- * @vortex-os/base base framework entry point for VortEX.
3
- *
4
- * This is the initial release (0.0.1). The full v0.1.0 will aggregate the
5
- * framework modules (core, slash-commands, til, decision-log, memory-system,
6
- * runbooks, data-lint, link-rewriter, index-generator, report-generator,
7
- * ai-coding-pitfalls, tool-rules, session-rituals) into a single importable
8
- * surface, plus an add-on discovery hook. See
9
- * https://github.com/dydan77/vortex/blob/main/docs/phase-10-split-plan.md
10
- * (Phase 10 split plan v2 — capability-cluster) for the shape.
11
- *
12
- * Until v0.1.0 lands, this package exposes framework metadata only.
13
- */
14
- export declare const VORTEX_BASE_VERSION: "0.0.1";
15
- /** Framework metadata available at install time, without pulling in any module. */
16
- export interface VortexFrameworkMeta {
17
- readonly version: string;
18
- readonly framework: "vortex-os";
19
- readonly fullReleaseTarget: string;
20
- readonly designReference: string;
21
- readonly repository: string;
22
- }
23
- export declare const FRAMEWORK_META: VortexFrameworkMeta;
24
- //# sourceMappingURL=index.d.ts.map
4
+ * Three-tier privacy classification used across VortEX.
5
+ *
6
+ * - `public` — safe to share with anyone, including in published repositories.
7
+ * - `internal` visible to the operator and their organization; not for public release.
8
+ * - `personal` — personal data; never shared, never published.
9
+ */
10
+ declare const Privacy: {
11
+ readonly Public: "public";
12
+ readonly Internal: "internal";
13
+ readonly Personal: "personal";
14
+ };
15
+ type Privacy = (typeof Privacy)[keyof typeof Privacy];
16
+ /**
17
+ * Parsed markdown document with separated YAML frontmatter and body.
18
+ */
19
+ interface FrontmatterDoc<T = Record<string, unknown>> {
20
+ frontmatter: T & {
21
+ privacy?: Privacy;
22
+ };
23
+ body: string;
24
+ }
25
+ /**
26
+ * Resolved paths for a VortEX repository root.
27
+ *
28
+ * Every module receives a `ModuleContext` from the host (CLI, plugin runtime,
29
+ * or test harness) and resolves its own paths against it. Modules must not
30
+ * derive paths by string manipulation against an assumed layout.
31
+ */
32
+ interface ModuleContext {
33
+ repoRoot: string;
34
+ agentDir: string;
35
+ dataDir: string;
36
+ modulesDir: string;
37
+ pluginsDir: string;
38
+ }
39
+
40
+ /**
41
+ * Parse a markdown source into its YAML frontmatter and body. If no frontmatter
42
+ * fence is present, returns an empty frontmatter object and the source unchanged.
43
+ *
44
+ * A leading UTF-8 BOM is stripped before matching. Windows-authored files
45
+ * frequently begin with ``, which would otherwise prevent the fence
46
+ * regex from anchoring to `---` at byte 0.
47
+ */
48
+ declare function parseFrontmatter<T = Record<string, unknown>>(source: string): FrontmatterDoc<T>;
49
+ /**
50
+ * Serialize a `FrontmatterDoc` back into markdown text. If the frontmatter is
51
+ * empty, the body is returned unchanged (no empty fence is emitted).
52
+ */
53
+ declare function serializeFrontmatter<T = Record<string, unknown>>(doc: FrontmatterDoc<T>): string;
54
+
55
+ /**
56
+ * Returns true if a document with `docPrivacy` is visible to a viewer authorized
57
+ * at `viewerPrivacy`. Viewers see content at or below their own level.
58
+ *
59
+ * - `public` viewer sees → public only
60
+ * - `internal` viewer sees → public + internal
61
+ * - `personal` viewer sees → all three
62
+ */
63
+ declare function isVisibleAt(docPrivacy: Privacy, viewerPrivacy: Privacy): boolean;
64
+ /**
65
+ * Returns the more-restrictive of two privacy levels.
66
+ * Useful when combining content from multiple sources for sharing.
67
+ */
68
+ declare function maxPrivacy(a: Privacy, b: Privacy): Privacy;
69
+ /**
70
+ * Coerce an unknown value (e.g. user input or untyped frontmatter) into a
71
+ * valid `Privacy`. Falls back to `internal` by default — the safest default
72
+ * for unclassified content.
73
+ */
74
+ declare function normalizePrivacy(value: unknown, fallback?: Privacy): Privacy;
75
+
76
+ /**
77
+ * Build a `ModuleContext` for the given VortEX repository root. The root is
78
+ * resolved to an absolute path; standard subdirectories are derived by
79
+ * convention and are not guaranteed to exist on disk.
80
+ */
81
+ declare function makeContext(repoRoot: string): ModuleContext;
82
+ /**
83
+ * Resolve the directory of a named module within the repository.
84
+ */
85
+ declare function moduleDir(ctx: ModuleContext, moduleName: string): string;
86
+
87
+ /**
88
+ * Instance configuration for VortEX, read from `<agentDir>/vortex.json`.
89
+ *
90
+ * The config exists to give the user control over the **auto-maintained**
91
+ * operational behaviors (see `AGENT.md` → "Default behaviors") without
92
+ * touching code: toggle any auto-record off, and declare how this machine's
93
+ * environment is detected. A fresh instance has no config file — everything
94
+ * is on, no environment — so the framework works out of the box and the file
95
+ * is purely opt-in tuning.
96
+ */
97
+ /** Which auto-maintained operational records are enabled. Default: all on. */
98
+ interface AutoRecordConfig {
99
+ readonly sessionStart: boolean;
100
+ readonly worklog: boolean;
101
+ readonly decision: boolean;
102
+ readonly ambientRecall: boolean;
103
+ /**
104
+ * Catch-up: at session start, ingest conversation transcripts that have not
105
+ * been archived yet (the host's own machine only). Text is stored
106
+ * immediately; vectorization is deferred to recall/rebuild so start stays
107
+ * fast. Off → no automatic ingest; the archive only grows when explicitly
108
+ * rebuilt.
109
+ */
110
+ readonly archive: boolean;
111
+ }
112
+ /**
113
+ * One environment label plus the signal that selects it. Rules are evaluated
114
+ * in order; the first match wins. Generalizes the operator's home/work
115
+ * `Test-Path` pattern into a portable, declarative form.
116
+ */
117
+ interface EnvironmentRule {
118
+ readonly label: string;
119
+ /** Match when this absolute path exists on the machine. */
120
+ readonly pathExists?: string;
121
+ /** Match when the OS hostname equals this (case-insensitive). */
122
+ readonly hostname?: string;
123
+ /**
124
+ * Match when an environment variable is set. A bare string matches on
125
+ * presence; the object form additionally requires a specific value.
126
+ */
127
+ readonly envVar?: string | {
128
+ readonly name: string;
129
+ readonly equals?: string;
130
+ };
131
+ }
132
+ interface VortexConfig {
133
+ readonly autoRecord: AutoRecordConfig;
134
+ readonly environments: readonly EnvironmentRule[];
135
+ }
136
+ /** Path of the instance config file: `<agentDir>/vortex.json`. */
137
+ declare function vortexConfigPath(ctx: ModuleContext): string;
138
+ /**
139
+ * Load the instance config, merged over defaults. A missing, unreadable, or
140
+ * invalid file yields defaults (everything on, no environments) rather than
141
+ * throwing — config is opt-in tuning, never a prerequisite.
142
+ */
143
+ declare function loadVortexConfig(ctx: ModuleContext): VortexConfig;
144
+ /**
145
+ * Resolve the active environment label from the config rules (first match
146
+ * wins), or null when none match. Pure — the caller injects the signals
147
+ * (hostname, env vars, a path-existence probe) so this stays testable and
148
+ * free of direct OS access.
149
+ */
150
+ declare function resolveEnvironment(config: VortexConfig, signals: {
151
+ readonly hostname?: string;
152
+ readonly env?: Record<string, string | undefined>;
153
+ readonly pathExists?: (p: string) => boolean;
154
+ }): string | null;
155
+
156
+ //# sourceMappingURL=index.d.ts.map
157
+
158
+ type index_d$d_AutoRecordConfig = AutoRecordConfig;
159
+ type index_d$d_EnvironmentRule = EnvironmentRule;
160
+ type index_d$d_FrontmatterDoc<T = Record<string, unknown>> = FrontmatterDoc<T>;
161
+ type index_d$d_ModuleContext = ModuleContext;
162
+ type index_d$d_Privacy = Privacy;
163
+ type index_d$d_VortexConfig = VortexConfig;
164
+ declare const index_d$d_isVisibleAt: typeof isVisibleAt;
165
+ declare const index_d$d_loadVortexConfig: typeof loadVortexConfig;
166
+ declare const index_d$d_makeContext: typeof makeContext;
167
+ declare const index_d$d_maxPrivacy: typeof maxPrivacy;
168
+ declare const index_d$d_moduleDir: typeof moduleDir;
169
+ declare const index_d$d_normalizePrivacy: typeof normalizePrivacy;
170
+ declare const index_d$d_parseFrontmatter: typeof parseFrontmatter;
171
+ declare const index_d$d_resolveEnvironment: typeof resolveEnvironment;
172
+ declare const index_d$d_serializeFrontmatter: typeof serializeFrontmatter;
173
+ declare const index_d$d_vortexConfigPath: typeof vortexConfigPath;
174
+ declare namespace index_d$d {
175
+ export { type index_d$d_AutoRecordConfig as AutoRecordConfig, type index_d$d_EnvironmentRule as EnvironmentRule, type index_d$d_FrontmatterDoc as FrontmatterDoc, type index_d$d_ModuleContext as ModuleContext, type index_d$d_Privacy as Privacy, type index_d$d_VortexConfig as VortexConfig, index_d$d_isVisibleAt as isVisibleAt, index_d$d_loadVortexConfig as loadVortexConfig, index_d$d_makeContext as makeContext, index_d$d_maxPrivacy as maxPrivacy, index_d$d_moduleDir as moduleDir, index_d$d_normalizePrivacy as normalizePrivacy, index_d$d_parseFrontmatter as parseFrontmatter, index_d$d_resolveEnvironment as resolveEnvironment, index_d$d_serializeFrontmatter as serializeFrontmatter, index_d$d_vortexConfigPath as vortexConfigPath };
176
+ }
177
+
178
+ /**
179
+ * Declaration of a single argument expected by a command.
180
+ *
181
+ * Argument parsing in Phase 2 is positional: arguments are read from the
182
+ * tokens following the command name, in declaration order. Named flags
183
+ * (e.g. `--foo bar`) are not handled at this layer — hosts that need them
184
+ * can pre-process input before calling `runSlash`.
185
+ */
186
+ interface CommandArg {
187
+ readonly name: string;
188
+ readonly description: string;
189
+ readonly required?: boolean;
190
+ }
191
+ /**
192
+ * Value passed to a command's handler.
193
+ *
194
+ * - `raw` — the full input string with any leading `/` removed.
195
+ * - `args` — positional arguments matched against the command's declared
196
+ * `args` schema, keyed by argument name.
197
+ * - `rest` — the unparsed trailing portion of the input (everything after
198
+ * the command name), provided as-is for commands that prefer to handle
199
+ * their own parsing.
200
+ * - `context` — resolved repository paths from `@vortex-os/core.makeContext`.
201
+ */
202
+ interface CommandInput {
203
+ readonly raw: string;
204
+ readonly args: Readonly<Record<string, string>>;
205
+ readonly rest: string;
206
+ readonly context: ModuleContext;
207
+ }
208
+ /**
209
+ * A registrable, callable command.
210
+ */
211
+ interface Command<Result = unknown> {
212
+ readonly name: string;
213
+ readonly description: string;
214
+ readonly args?: readonly CommandArg[];
215
+ readonly handler: (input: CommandInput) => Result | Promise<Result>;
216
+ }
217
+
218
+ /**
219
+ * Holds registered commands and looks them up by name.
220
+ *
221
+ * Names are matched exactly. Registering two commands with the same name
222
+ * is an error — the second call throws. Callers that want to override an
223
+ * existing command must `unregister` first.
224
+ */
225
+ declare class CommandRegistry {
226
+ private readonly commands;
227
+ register(command: Command): void;
228
+ unregister(name: string): boolean;
229
+ has(name: string): boolean;
230
+ get(name: string): Command | undefined;
231
+ list(): readonly Command[];
232
+ }
233
+
234
+ interface RunOptions {
235
+ readonly registry: CommandRegistry;
236
+ readonly context: ModuleContext;
237
+ }
238
+ /**
239
+ * Thrown by `runSlash` when the requested command name does not exist
240
+ * in the registry. The unknown name is exposed for caller diagnostics.
241
+ */
242
+ declare class CommandNotFoundError extends Error {
243
+ readonly commandName: string;
244
+ constructor(commandName: string);
245
+ }
246
+ /**
247
+ * Parse an input string and dispatch the matching command.
248
+ *
249
+ * Accepted forms (leading slash optional):
250
+ * "name"
251
+ * "/name"
252
+ * "name arg1 arg2 ..."
253
+ *
254
+ * Returns whatever the command's handler returns (awaited if it is async).
255
+ */
256
+ declare function runSlash(input: string, { registry, context }: RunOptions): Promise<unknown>;
257
+
258
+ //# sourceMappingURL=index.d.ts.map
259
+
260
+ type index_d$c_Command<Result = unknown> = Command<Result>;
261
+ type index_d$c_CommandArg = CommandArg;
262
+ type index_d$c_CommandInput = CommandInput;
263
+ type index_d$c_CommandNotFoundError = CommandNotFoundError;
264
+ declare const index_d$c_CommandNotFoundError: typeof CommandNotFoundError;
265
+ type index_d$c_CommandRegistry = CommandRegistry;
266
+ declare const index_d$c_CommandRegistry: typeof CommandRegistry;
267
+ type index_d$c_RunOptions = RunOptions;
268
+ declare const index_d$c_runSlash: typeof runSlash;
269
+ declare namespace index_d$c {
270
+ export { type index_d$c_Command as Command, type index_d$c_CommandArg as CommandArg, type index_d$c_CommandInput as CommandInput, index_d$c_CommandNotFoundError as CommandNotFoundError, index_d$c_CommandRegistry as CommandRegistry, type index_d$c_RunOptions as RunOptions, index_d$c_runSlash as runSlash };
271
+ }
272
+
273
+ /**
274
+ * Canonical memory categories.
275
+ *
276
+ * - `user` — facts about the operator (role, preferences, working style).
277
+ * - `feedback` — corrections and validated approaches from the operator.
278
+ * - `project` — ongoing work context (goals, deadlines, stakeholders).
279
+ * - `reference` — pointers to external systems (dashboards, repos, channels).
280
+ *
281
+ * Hosts may extend the set, but stability of these four is preserved.
282
+ */
283
+ declare const MemoryType: {
284
+ readonly User: "user";
285
+ readonly Feedback: "feedback";
286
+ readonly Project: "project";
287
+ readonly Reference: "reference";
288
+ };
289
+ type MemoryType = (typeof MemoryType)[keyof typeof MemoryType];
290
+ /**
291
+ * Frontmatter required on every memory file.
292
+ *
293
+ * `name` should match the file id (filename without `.md`). `description`
294
+ * is a single-line summary used in the generated index. `type` places the
295
+ * memory in one of the canonical categories. Additional keys (e.g.
296
+ * `originSessionId`, `created`, custom tags) are tolerated and preserved
297
+ * on round-trip but are not required.
298
+ */
299
+ interface MemoryFrontmatter {
300
+ name: string;
301
+ description: string;
302
+ type: MemoryType;
303
+ [key: string]: unknown;
304
+ }
305
+ /**
306
+ * A parsed memory document, ready to be written back or rendered.
307
+ *
308
+ * `id` is the filename stem (no `.md`); it is the address by which the
309
+ * store retrieves and overwrites the memory.
310
+ */
311
+ interface Memory {
312
+ id: string;
313
+ frontmatter: MemoryFrontmatter;
314
+ body: string;
315
+ }
316
+
317
+ /**
318
+ * A directory-backed memory store.
319
+ *
320
+ * Each memory lives in a single `.md` file. The `MEMORY.md` (generated index)
321
+ * and `_INDEX.md` (Obsidian-friendly index) files are excluded — they are
322
+ * derived views, not memories themselves.
323
+ */
324
+ declare class MemoryStore {
325
+ readonly dir: string;
326
+ constructor(dir: string);
327
+ /** Ensure the backing directory exists. Safe to call repeatedly. */
328
+ ensure(): Promise<void>;
329
+ /** List memory ids (filename stems), sorted lexicographically. */
330
+ list(): Promise<readonly string[]>;
331
+ /** Read a memory by id. Throws if the file does not exist. */
332
+ read(id: string): Promise<Memory>;
333
+ /** Write (or overwrite) a memory. Ensures the directory exists first. */
334
+ write(memory: Memory): Promise<void>;
335
+ /** Delete a memory. Returns false if it did not exist. */
336
+ delete(id: string): Promise<boolean>;
337
+ /** Test whether a memory exists by id. */
338
+ has(id: string): Promise<boolean>;
339
+ /** Absolute path of a memory file (file may not exist). */
340
+ pathFor(id: string): string;
341
+ }
342
+
343
+ interface WriteMemoryIndexOptions {
344
+ /** Top-level heading. Defaults to "Memory Index". */
345
+ title?: string;
346
+ /** Optional text placed above the heading (e.g. an HTML comment). */
347
+ preamble?: string;
348
+ }
349
+ /**
350
+ * Render a `MEMORY.md` index from the entries currently in `store` and
351
+ * write it to `<store.dir>/MEMORY.md`. Existing `MEMORY.md` is overwritten.
352
+ *
353
+ * The index is a flat bullet list: one line per memory, linking to the
354
+ * memory file and including the memory's `description`.
355
+ */
356
+ declare function writeMemoryIndex(store: MemoryStore, options?: WriteMemoryIndexOptions): Promise<void>;
357
+
358
+ /**
359
+ * Difference between two memory stores. All three lists may be empty,
360
+ * which means the stores are in sync.
361
+ */
362
+ interface SyncDiff {
363
+ /** Ids present in `a` but not in `b`. */
364
+ onlyInA: readonly string[];
365
+ /** Ids present in `b` but not in `a`. */
366
+ onlyInB: readonly string[];
367
+ /** Ids present in both but whose raw file contents differ. */
368
+ changed: readonly string[];
369
+ }
370
+ /**
371
+ * Compute the diff between two memory stores. Neither store is modified.
372
+ *
373
+ * `changed` is determined by exact byte comparison of the underlying
374
+ * `.md` files. Frontmatter ordering or whitespace differences therefore
375
+ * count as a change — callers that want semantic equality should
376
+ * re-serialize through `parseFrontmatter` / `serializeFrontmatter` before
377
+ * comparing.
378
+ */
379
+ declare function diffStores(a: MemoryStore, b: MemoryStore): Promise<SyncDiff>;
380
+
381
+ //# sourceMappingURL=index.d.ts.map
382
+
383
+ type index_d$b_Memory = Memory;
384
+ type index_d$b_MemoryFrontmatter = MemoryFrontmatter;
385
+ type index_d$b_MemoryStore = MemoryStore;
386
+ declare const index_d$b_MemoryStore: typeof MemoryStore;
387
+ type index_d$b_MemoryType = MemoryType;
388
+ type index_d$b_SyncDiff = SyncDiff;
389
+ type index_d$b_WriteMemoryIndexOptions = WriteMemoryIndexOptions;
390
+ declare const index_d$b_diffStores: typeof diffStores;
391
+ declare const index_d$b_writeMemoryIndex: typeof writeMemoryIndex;
392
+ declare namespace index_d$b {
393
+ export { type index_d$b_Memory as Memory, type index_d$b_MemoryFrontmatter as MemoryFrontmatter, index_d$b_MemoryStore as MemoryStore, type index_d$b_MemoryType as MemoryType, type index_d$b_SyncDiff as SyncDiff, type index_d$b_WriteMemoryIndexOptions as WriteMemoryIndexOptions, index_d$b_diffStores as diffStores, index_d$b_writeMemoryIndex as writeMemoryIndex };
394
+ }
395
+
396
+ type Severity = "error" | "warning" | "info";
397
+ /**
398
+ * A single problem found by a rule.
399
+ */
400
+ interface LintFinding {
401
+ readonly rule: string;
402
+ readonly severity: Severity;
403
+ readonly file: string;
404
+ readonly line?: number;
405
+ readonly message: string;
406
+ }
407
+ /**
408
+ * Input passed to a rule's `check` function.
409
+ */
410
+ interface LintInput {
411
+ readonly file: string;
412
+ readonly content: string;
413
+ }
414
+ /**
415
+ * A registrable lint rule.
416
+ *
417
+ * Rules are pure: same input produces the same findings, no side effects.
418
+ * `check` may be async — useful for rules that need to consult other
419
+ * files (e.g. resolving wiki links).
420
+ */
421
+ interface LintRule {
422
+ readonly id: string;
423
+ readonly description: string;
424
+ readonly check: (input: LintInput) => readonly LintFinding[] | Promise<readonly LintFinding[]>;
425
+ }
426
+ /**
427
+ * Aggregated result of a lint run.
428
+ */
429
+ interface LintReport {
430
+ readonly findings: readonly LintFinding[];
431
+ readonly filesScanned: number;
432
+ readonly rulesRun: number;
433
+ readonly durationMs: number;
434
+ }
435
+
436
+ interface LintOptions {
437
+ /** Directory to scan recursively. */
438
+ readonly dir: string;
439
+ /** Rules to apply to every file. */
440
+ readonly rules: readonly LintRule[];
441
+ /** File extensions to include. Defaults to `[".md"]`. */
442
+ readonly extensions?: readonly string[];
443
+ }
444
+ /**
445
+ * Recursively scan `dir` for files with the configured extensions and run
446
+ * every rule against each file. Findings from all rules are aggregated into
447
+ * a single `LintReport`. Order of findings within the report is not
448
+ * guaranteed beyond "file by file, rule by rule".
449
+ */
450
+ declare function lintDirectory(options: LintOptions): Promise<LintReport>;
451
+
452
+ interface RequireFrontmatterOptions {
453
+ /** Frontmatter keys that must be present and non-empty. */
454
+ readonly required: readonly string[];
455
+ }
456
+ /**
457
+ * Rule that ensures the listed frontmatter keys are present and non-empty.
458
+ * Empty string, null, and undefined values all fail the check; `0`, `false`,
459
+ * and structured values (arrays, objects) pass as long as they exist.
460
+ */
461
+ declare function requireFrontmatter(options: RequireFrontmatterOptions): LintRule;
462
+
463
+ /**
464
+ * Rule that ensures, if a `privacy` frontmatter field is present, its value
465
+ * is one of the canonical three: `public`, `internal`, `personal`.
466
+ * Documents without any `privacy` field pass.
467
+ */
468
+ declare function privacyValid(): LintRule;
469
+
470
+ interface WikiLinkResolvesOptions {
471
+ /** Directory under which valid link targets live. Searched recursively. */
472
+ readonly searchRoot: string;
473
+ /** Target file extensions. Defaults to `[".md"]`. */
474
+ readonly extensions?: readonly string[];
475
+ }
476
+ /**
477
+ * Rule that checks wiki-style links `[[target]]` resolve to an existing
478
+ * file under `searchRoot` (by basename match, recursive). Aliases
479
+ * (`[[target|alias]]`) and anchors (`[[target#section]]`) are stripped
480
+ * before lookup.
481
+ *
482
+ * The target index is built lazily on the first invocation and cached for
483
+ * the lifetime of the rule instance.
484
+ */
485
+ declare function wikiLinkResolves(options: WikiLinkResolvesOptions): LintRule;
486
+
487
+ //# sourceMappingURL=index.d.ts.map
488
+
489
+ type index_d$a_LintFinding = LintFinding;
490
+ type index_d$a_LintInput = LintInput;
491
+ type index_d$a_LintOptions = LintOptions;
492
+ type index_d$a_LintReport = LintReport;
493
+ type index_d$a_LintRule = LintRule;
494
+ type index_d$a_RequireFrontmatterOptions = RequireFrontmatterOptions;
495
+ type index_d$a_Severity = Severity;
496
+ type index_d$a_WikiLinkResolvesOptions = WikiLinkResolvesOptions;
497
+ declare const index_d$a_lintDirectory: typeof lintDirectory;
498
+ declare const index_d$a_privacyValid: typeof privacyValid;
499
+ declare const index_d$a_requireFrontmatter: typeof requireFrontmatter;
500
+ declare const index_d$a_wikiLinkResolves: typeof wikiLinkResolves;
501
+ declare namespace index_d$a {
502
+ export { type index_d$a_LintFinding as LintFinding, type index_d$a_LintInput as LintInput, type index_d$a_LintOptions as LintOptions, type index_d$a_LintReport as LintReport, type index_d$a_LintRule as LintRule, type index_d$a_RequireFrontmatterOptions as RequireFrontmatterOptions, type index_d$a_Severity as Severity, type index_d$a_WikiLinkResolvesOptions as WikiLinkResolvesOptions, index_d$a_lintDirectory as lintDirectory, index_d$a_privacyValid as privacyValid, index_d$a_requireFrontmatter as requireFrontmatter, index_d$a_wikiLinkResolves as wikiLinkResolves };
503
+ }
504
+
505
+ type PitfallSeverity = "info" | "warning" | "error";
506
+ /**
507
+ * A single AI coding pitfall.
508
+ *
509
+ * Pitfalls describe failure modes that recur across AI coding work, along
510
+ * with how to recognize them and what to do instead. They are catalog
511
+ * entries, not active detectors.
512
+ */
513
+ interface Pitfall {
514
+ readonly id: string;
515
+ readonly title: string;
516
+ readonly category: string;
517
+ readonly severity: PitfallSeverity;
518
+ readonly signal: string;
519
+ readonly cause: string;
520
+ readonly mitigation: string;
521
+ /** Free-form trailing markdown (examples, notes); empty string if none. */
522
+ readonly body: string;
523
+ }
524
+ /**
525
+ * Frontmatter shape expected on a pitfall `.md` file.
526
+ *
527
+ * `id` is optional in the source — if omitted, the filename stem is used.
528
+ */
529
+ interface PitfallFrontmatter {
530
+ id?: string;
531
+ title: string;
532
+ category: string;
533
+ severity: PitfallSeverity;
534
+ signal: string;
535
+ cause: string;
536
+ mitigation: string;
537
+ }
538
+
539
+ /**
540
+ * Directory-backed pitfall catalog. One `.md` file per pitfall.
541
+ *
542
+ * The catalog reads all `.md` files in `dir` on each query (no caching).
543
+ * Callers that need cached behavior should wrap or call `list()` once and
544
+ * reuse the result.
545
+ */
546
+ declare class PitfallCatalog {
547
+ readonly dir: string;
548
+ constructor(dir: string);
549
+ /** Load every pitfall in the directory, sorted by id. */
550
+ list(): Promise<readonly Pitfall[]>;
551
+ /** Load a single pitfall by id. Returns `undefined` if not found. */
552
+ get(id: string): Promise<Pitfall | undefined>;
553
+ /** Return only pitfalls matching the given category. */
554
+ filterByCategory(category: string): Promise<readonly Pitfall[]>;
555
+ private entries;
556
+ private loadFile;
557
+ }
558
+
559
+ //# sourceMappingURL=index.d.ts.map
560
+
561
+ type index_d$9_Pitfall = Pitfall;
562
+ type index_d$9_PitfallCatalog = PitfallCatalog;
563
+ declare const index_d$9_PitfallCatalog: typeof PitfallCatalog;
564
+ type index_d$9_PitfallFrontmatter = PitfallFrontmatter;
565
+ type index_d$9_PitfallSeverity = PitfallSeverity;
566
+ declare namespace index_d$9 {
567
+ export { type index_d$9_Pitfall as Pitfall, index_d$9_PitfallCatalog as PitfallCatalog, type index_d$9_PitfallFrontmatter as PitfallFrontmatter, type index_d$9_PitfallSeverity as PitfallSeverity };
568
+ }
569
+
570
+ type ToolRuleSeverity = "advisory" | "strict" | "absolute";
571
+ /**
572
+ * A single tool-usage rule.
573
+ *
574
+ * Tool rules express constraints the operator wants agents to obey when
575
+ * invoking specific tools. They are catalog entries, not active enforcers.
576
+ */
577
+ interface ToolRule {
578
+ readonly id: string;
579
+ readonly title: string;
580
+ readonly scope: string;
581
+ readonly severity: ToolRuleSeverity;
582
+ readonly condition: string;
583
+ readonly action: string;
584
+ /** Free-form trailing markdown (examples, notes); empty string if none. */
585
+ readonly body: string;
586
+ }
587
+ /**
588
+ * Frontmatter shape expected on a tool-rule `.md` file.
589
+ *
590
+ * `id` is optional in the source — if omitted, the filename stem is used.
591
+ */
592
+ interface ToolRuleFrontmatter {
593
+ id?: string;
594
+ title: string;
595
+ scope: string;
596
+ severity: ToolRuleSeverity;
597
+ condition: string;
598
+ action: string;
599
+ }
600
+
601
+ /**
602
+ * Directory-backed tool-rule catalog. One `.md` file per rule.
603
+ *
604
+ * The catalog reads all `.md` files in `dir` on each query (no caching).
605
+ */
606
+ declare class ToolRuleCatalog {
607
+ readonly dir: string;
608
+ constructor(dir: string);
609
+ /** Load every rule in the directory, sorted by id. */
610
+ list(): Promise<readonly ToolRule[]>;
611
+ /** Load a single rule by id. Returns `undefined` if not found. */
612
+ get(id: string): Promise<ToolRule | undefined>;
613
+ /** Return only rules whose `scope` matches exactly. */
614
+ filterByScope(scope: string): Promise<readonly ToolRule[]>;
615
+ /** Return only rules whose `severity` matches. */
616
+ filterBySeverity(severity: ToolRuleSeverity): Promise<readonly ToolRule[]>;
617
+ private entries;
618
+ private loadFile;
619
+ }
620
+
621
+ //# sourceMappingURL=index.d.ts.map
622
+
623
+ type index_d$8_ToolRule = ToolRule;
624
+ type index_d$8_ToolRuleCatalog = ToolRuleCatalog;
625
+ declare const index_d$8_ToolRuleCatalog: typeof ToolRuleCatalog;
626
+ type index_d$8_ToolRuleFrontmatter = ToolRuleFrontmatter;
627
+ type index_d$8_ToolRuleSeverity = ToolRuleSeverity;
628
+ declare namespace index_d$8 {
629
+ export { type index_d$8_ToolRule as ToolRule, index_d$8_ToolRuleCatalog as ToolRuleCatalog, type index_d$8_ToolRuleFrontmatter as ToolRuleFrontmatter, type index_d$8_ToolRuleSeverity as ToolRuleSeverity };
630
+ }
631
+
632
+ interface RenderOptions {
633
+ /** Target viewer privacy level. Content above this is stripped. */
634
+ readonly targetPrivacy: Privacy;
635
+ /**
636
+ * Optional HTML template. The placeholders `{{title}}` and `{{content}}`
637
+ * are replaced. If omitted, a minimal default template is used.
638
+ */
639
+ readonly template?: string;
640
+ /** Optional title for the `{{title}}` placeholder. */
641
+ readonly title?: string;
642
+ }
643
+ type WarningKind = "section-stripped" | "document-not-visible";
644
+ interface RenderWarning {
645
+ readonly kind: WarningKind;
646
+ readonly reason: string;
647
+ }
648
+ interface RenderResult {
649
+ /** Rendered HTML. Empty string if the document is not visible at target. */
650
+ readonly html: string;
651
+ /** Effective document privacy (frontmatter value or default `internal`). */
652
+ readonly documentPrivacy: Privacy;
653
+ /** Whether the document was visible at the requested target privacy. */
654
+ readonly visible: boolean;
655
+ /** Warnings collected during render (stripped sections, skipped docs). */
656
+ readonly warnings: readonly RenderWarning[];
657
+ }
658
+
659
+ /**
660
+ * Render a markdown source to HTML, applying document-level and
661
+ * section-level privacy filtering before conversion.
662
+ *
663
+ * If the document's own privacy exceeds the target, an empty result is
664
+ * returned with a `document-not-visible` warning and no body conversion
665
+ * is performed.
666
+ */
667
+ declare function renderHtml(source: string, options: RenderOptions): Promise<RenderResult>;
668
+
669
+ interface StripResult {
670
+ readonly content: string;
671
+ readonly warnings: readonly RenderWarning[];
672
+ }
673
+ /**
674
+ * Remove section blocks whose declared privacy exceeds the target.
675
+ *
676
+ * Each section is delimited by `<!-- vortex:privacy LEVEL -->` and
677
+ * `<!-- /vortex:privacy -->`. Sections at or below target privacy keep
678
+ * their body (markers themselves are removed). Sections above target are
679
+ * stripped entirely and a `section-stripped` warning is reported.
680
+ *
681
+ * Sections do not nest; the parser does not support nesting.
682
+ */
683
+ declare function stripPrivateSections(content: string, targetPrivacy: Privacy): StripResult;
684
+
685
+ /**
686
+ * Minimal default template. Hosts that want styling, navigation, or
687
+ * additional metadata should pass a custom `template` to `renderHtml`.
688
+ */
689
+ declare const DEFAULT_TEMPLATE = "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<title>{{title}}</title>\n</head>\n<body>\n{{content}}\n</body>\n</html>\n";
690
+ /**
691
+ * Replace `{{name}}` placeholders in a template string with values from
692
+ * `vars`. Missing keys are replaced with the empty string.
693
+ */
694
+ declare function applyTemplate(template: string, vars: Readonly<Record<string, string>>): string;
695
+
696
+ //# sourceMappingURL=index.d.ts.map
697
+
698
+ declare const index_d$7_DEFAULT_TEMPLATE: typeof DEFAULT_TEMPLATE;
699
+ type index_d$7_RenderOptions = RenderOptions;
700
+ type index_d$7_RenderResult = RenderResult;
701
+ type index_d$7_RenderWarning = RenderWarning;
702
+ type index_d$7_StripResult = StripResult;
703
+ type index_d$7_WarningKind = WarningKind;
704
+ declare const index_d$7_applyTemplate: typeof applyTemplate;
705
+ declare const index_d$7_renderHtml: typeof renderHtml;
706
+ declare const index_d$7_stripPrivateSections: typeof stripPrivateSections;
707
+ declare namespace index_d$7 {
708
+ export { index_d$7_DEFAULT_TEMPLATE as DEFAULT_TEMPLATE, type index_d$7_RenderOptions as RenderOptions, type index_d$7_RenderResult as RenderResult, type index_d$7_RenderWarning as RenderWarning, type index_d$7_StripResult as StripResult, type index_d$7_WarningKind as WarningKind, index_d$7_applyTemplate as applyTemplate, index_d$7_renderHtml as renderHtml, index_d$7_stripPrivateSections as stripPrivateSections };
709
+ }
710
+
711
+ /**
712
+ * Frontmatter expected on a worklog file. All fields besides `type` are
713
+ * conventional — the store tolerates absence and surfaces what is present.
714
+ */
715
+ interface WorklogFrontmatter {
716
+ type: "worklog";
717
+ status?: string;
718
+ privacy?: string;
719
+ created?: string;
720
+ updated?: string;
721
+ tags?: readonly string[];
722
+ related?: readonly string[];
723
+ [key: string]: unknown;
724
+ }
725
+ /**
726
+ * A parsed worklog entry.
727
+ *
728
+ * `date` is the ISO date portion of the filename (`YYYY-MM-DD`).
729
+ * `keyword` is the trailing slug portion of the filename without `.md`.
730
+ * `path` is the absolute path of the file on disk.
731
+ */
732
+ interface WorklogEntry {
733
+ readonly date: string;
734
+ readonly keyword: string;
735
+ readonly path: string;
736
+ readonly frontmatter: WorklogFrontmatter;
737
+ readonly body: string;
738
+ }
739
+
740
+ /**
741
+ * Directory-backed worklog store. Expected layout: `<rootDir>/YYYY/MM/YYYY-MM-DD-keyword.md`.
742
+ *
743
+ * The store treats missing subdirectories as empty, never as errors —
744
+ * a brand-new month with no entries returns an empty list, not a throw.
745
+ */
746
+ declare class WorklogStore {
747
+ readonly rootDir: string;
748
+ constructor(rootDir: string);
749
+ /** All entries across all years and months, sorted by date ascending. */
750
+ list(): Promise<readonly WorklogEntry[]>;
751
+ /** Entries within one calendar month. */
752
+ listByMonth(year: number, month: number): Promise<readonly WorklogEntry[]>;
753
+ /**
754
+ * The first entry matching `date` (`YYYY-MM-DD`). If multiple files exist
755
+ * for that date with different keywords, the lexicographically-first one
756
+ * is returned. Use `list()` when all are needed.
757
+ */
758
+ get(date: string): Promise<WorklogEntry | undefined>;
759
+ /** Most recent entry by date (descending), then keyword (descending). */
760
+ getLatest(): Promise<WorklogEntry | undefined>;
761
+ /** Resolve the file path for a given (date, keyword), without creating it. */
762
+ pathFor(date: string, keyword: string): string;
763
+ private listSubdirs;
764
+ private entriesIn;
765
+ }
766
+
767
+ /**
768
+ * Append a new section to the end of an existing worklog entry's file.
769
+ *
770
+ * The resulting markdown adds two leading blank lines, then `## <title>`,
771
+ * then a blank line, then the body. The entry's frontmatter is not touched.
772
+ *
773
+ * Returns the updated raw markdown that was written.
774
+ */
775
+ declare function appendSection(entry: WorklogEntry, title: string, body: string): Promise<string>;
776
+
777
+ //# sourceMappingURL=index.d.ts.map
778
+
779
+ type index_d$6_WorklogEntry = WorklogEntry;
780
+ type index_d$6_WorklogFrontmatter = WorklogFrontmatter;
781
+ type index_d$6_WorklogStore = WorklogStore;
782
+ declare const index_d$6_WorklogStore: typeof WorklogStore;
783
+ declare const index_d$6_appendSection: typeof appendSection;
784
+ declare namespace index_d$6 {
785
+ export { type index_d$6_WorklogEntry as WorklogEntry, type index_d$6_WorklogFrontmatter as WorklogFrontmatter, index_d$6_WorklogStore as WorklogStore, index_d$6_appendSection as appendSection };
786
+ }
787
+
788
+ /**
789
+ * Frontmatter expected on a Decision-Log entry. `type` is the only required
790
+ * field — the store tolerates absence of the rest and surfaces what is present.
791
+ *
792
+ * `privacy` defaults to `personal` by convention; callers should not rely on
793
+ * the store to enforce it.
794
+ */
795
+ interface DecisionLogFrontmatter {
796
+ type: "decision-log";
797
+ status?: "active" | "template" | "archived" | string;
798
+ privacy?: "personal" | "internal" | "public" | string;
799
+ created?: string;
800
+ updated?: string;
801
+ tags?: readonly string[];
802
+ related?: readonly string[];
803
+ [key: string]: unknown;
804
+ }
805
+ /**
806
+ * A parsed Decision-Log entry.
807
+ *
808
+ * `date` is the ISO date portion of the filename (`YYYY-MM-DD`).
809
+ * `slug` is the trailing portion of the filename without `.md`.
810
+ * `path` is the absolute path of the file on disk.
811
+ */
812
+ interface DecisionEntry {
813
+ readonly date: string;
814
+ readonly slug: string;
815
+ readonly path: string;
816
+ readonly frontmatter: DecisionLogFrontmatter;
817
+ readonly body: string;
818
+ }
819
+ /** Filter criteria for {@link DecisionStore.filter}. Empty filter matches all. */
820
+ interface DecisionFilter {
821
+ /** Match entries with `frontmatter.status` equal to this value. */
822
+ status?: string;
823
+ /** Match entries that contain this tag (case-sensitive). */
824
+ tag?: string;
825
+ /** Match entries with date >= this value (`YYYY-MM-DD`). */
826
+ fromDate?: string;
827
+ /** Match entries with date <= this value (`YYYY-MM-DD`). */
828
+ toDate?: string;
829
+ }
830
+ /** Input for {@link renderTemplate}. */
831
+ interface DecisionTemplateInput {
832
+ /** ISO date (`YYYY-MM-DD`). Also written into the body header. */
833
+ date: string;
834
+ /** Short kebab-style identifier used in the filename. */
835
+ slug: string;
836
+ /** One-line decision title shown as the H1. */
837
+ title: string;
838
+ /** Free-form area label (e.g. `work`, `personal`, `home-lab`). */
839
+ area?: string;
840
+ /** Extra tags appended after the default `decision-log` tag. */
841
+ tags?: readonly string[];
842
+ }
843
+
844
+ /**
845
+ * Directory-backed Decision-Log store. Expected layout is flat —
846
+ * `<rootDir>/YYYY-MM-DD-<slug>.md`, no year/month subdirectories.
847
+ *
848
+ * Non-entry files at the root (README, _TEMPLATE, anything not matching
849
+ * the date-prefix pattern) are silently ignored.
850
+ *
851
+ * A missing root directory returns an empty list rather than throwing,
852
+ * so a brand-new template clone with no entries is a normal state.
853
+ */
854
+ declare class DecisionStore {
855
+ readonly rootDir: string;
856
+ constructor(rootDir: string);
857
+ /** All entries, sorted by date ascending, then slug ascending. */
858
+ list(): Promise<readonly DecisionEntry[]>;
859
+ /**
860
+ * Entries whose filename starts with `date` (`YYYY-MM-DD`). Multiple
861
+ * decisions on the same day are returned in slug-ascending order.
862
+ */
863
+ getByDate(date: string): Promise<readonly DecisionEntry[]>;
864
+ /**
865
+ * The first entry matching `date` and (optionally) `slug`. When no slug is
866
+ * provided and multiple entries share the date, the lexicographically-first
867
+ * one is returned.
868
+ */
869
+ get(date: string, slug?: string): Promise<DecisionEntry | undefined>;
870
+ /** Most recent entry by date (descending), then slug (descending). */
871
+ getLatest(): Promise<DecisionEntry | undefined>;
872
+ /**
873
+ * Subset of entries matching the given criteria. Empty/undefined fields
874
+ * are ignored. Returns entries in the same order as {@link list}.
875
+ */
876
+ filter(criteria: DecisionFilter): Promise<readonly DecisionEntry[]>;
877
+ /** Resolve the file path for a given (date, slug), without creating it. */
878
+ pathFor(date: string, slug: string): string;
879
+ }
880
+
881
+ /**
882
+ * Render a new Decision-Log entry body using an obsidian-style template
883
+ * convention. Caller is responsible for writing the result to disk via
884
+ * {@link DecisionStore.pathFor}.
885
+ *
886
+ * The output deliberately mirrors a human-authored template so the
887
+ * shape stays consistent whether the entry was created by hand or by
888
+ * this helper.
889
+ */
890
+ declare function renderTemplate(input: DecisionTemplateInput): string;
891
+
892
+ //# sourceMappingURL=index.d.ts.map
893
+
894
+ type index_d$5_DecisionEntry = DecisionEntry;
895
+ type index_d$5_DecisionFilter = DecisionFilter;
896
+ type index_d$5_DecisionLogFrontmatter = DecisionLogFrontmatter;
897
+ type index_d$5_DecisionStore = DecisionStore;
898
+ declare const index_d$5_DecisionStore: typeof DecisionStore;
899
+ type index_d$5_DecisionTemplateInput = DecisionTemplateInput;
900
+ declare const index_d$5_renderTemplate: typeof renderTemplate;
901
+ declare namespace index_d$5 {
902
+ export { type index_d$5_DecisionEntry as DecisionEntry, type index_d$5_DecisionFilter as DecisionFilter, type index_d$5_DecisionLogFrontmatter as DecisionLogFrontmatter, index_d$5_DecisionStore as DecisionStore, type index_d$5_DecisionTemplateInput as DecisionTemplateInput, index_d$5_renderTemplate as renderTemplate };
903
+ }
904
+
905
+ /**
906
+ * One entry surfaced in a generated `_INDEX.md`.
907
+ */
908
+ interface IndexEntry {
909
+ /** Path relative to the directory being indexed (forward slashes). */
910
+ readonly relPath: string;
911
+ /** Filename without extension. */
912
+ readonly name: string;
913
+ /** Title — `# H1` from the body, or filename fallback. */
914
+ readonly title: string;
915
+ /** Short description — frontmatter `description`, or first body paragraph fallback. */
916
+ readonly description?: string;
917
+ /** Frontmatter `type` value, if present (e.g. `worklog`, `decision-log`, `memory`). */
918
+ readonly type?: string;
919
+ /** Frontmatter `updated` or `created` value, ISO `YYYY-MM-DD` when parseable. */
920
+ readonly updated?: string;
921
+ }
922
+ /**
923
+ * Options controlling {@link scanDirectory}.
924
+ */
925
+ interface ScanOptions {
926
+ /**
927
+ * Recurse into subdirectories. Default: false (flat scan).
928
+ *
929
+ * When true, the scanner descends into all subdirectories whose name does
930
+ * not start with `.` or `_`. The reserved files `README.md` and
931
+ * `_INDEX.md` are always skipped at every depth.
932
+ */
933
+ recursive?: boolean;
934
+ /**
935
+ * Filenames to skip in addition to the always-skipped reserved files
936
+ * (`README.md`, `_INDEX.md`). Comparison is case-sensitive on the basename.
937
+ */
938
+ skipFilenames?: readonly string[];
939
+ /**
940
+ * Filename prefixes to skip (e.g. `["_TEMPLATE"]`). Comparison is
941
+ * case-sensitive against the basename without extension.
942
+ */
943
+ skipPrefixes?: readonly string[];
944
+ }
945
+ /**
946
+ * Input for {@link renderIndex}.
947
+ */
948
+ interface RenderIndexInput {
949
+ /** Page H1 — typically the directory's human name (e.g. "Memory", "Worklog"). */
950
+ title: string;
951
+ /**
952
+ * One-line blockquote shown beneath the H1. Pass an empty string to omit
953
+ * the blockquote.
954
+ */
955
+ description?: string;
956
+ /** Entries to list. Order is preserved as given. */
957
+ entries: readonly IndexEntry[];
958
+ /** Frontmatter `updated` field. Default: today's date in `YYYY-MM-DD`. */
959
+ updated?: string;
960
+ /** Frontmatter `privacy` field. Default: `internal`. */
961
+ privacy?: "public" | "internal" | "personal" | string;
962
+ /** Extra frontmatter tags besides the default `[index]`. */
963
+ extraTags?: readonly string[];
964
+ /** Optional "관련" links rendered after the entry list. */
965
+ related?: readonly string[];
966
+ }
967
+
968
+ /**
969
+ * Scan a directory of markdown notes and produce one {@link IndexEntry}
970
+ * per file, sorted by `relPath` ascending.
971
+ *
972
+ * - Files whose names are `README.md` or `_INDEX.md` are always skipped.
973
+ * - Hidden entries (name starting with `.` or `_`, except `_TEMPLATE` which
974
+ * is still surfaced but can be excluded via `skipPrefixes: ["_TEMPLATE"]`)
975
+ * are not descended into when `recursive: true`.
976
+ * - A missing directory returns an empty list rather than throwing.
977
+ */
978
+ declare function scanDirectory(rootDir: string, opts?: ScanOptions): Promise<readonly IndexEntry[]>;
979
+
980
+ /**
981
+ * Render an `_INDEX.md` body from a {@link RenderIndexInput}.
982
+ *
983
+ * Output shape:
984
+ *
985
+ * ---
986
+ * type: index
987
+ * status: active
988
+ * privacy: <privacy>
989
+ * updated: <updated>
990
+ * tags: [index, <extraTags...>]
991
+ * ---
992
+ *
993
+ * # <title>
994
+ *
995
+ * > <description>
996
+ *
997
+ * ## 항목 (<count>)
998
+ *
999
+ * | 파일 | 설명 | 갱신 |
1000
+ * |---|---|---|
1001
+ * | [[<name>]] | <description or title> | <updated> |
1002
+ * ...
1003
+ *
1004
+ * ## 관련 (only if `related` is non-empty)
1005
+ *
1006
+ * - [[<related[0]>]]
1007
+ * ...
1008
+ *
1009
+ * Caller is responsible for writing this body to disk. The renderer never
1010
+ * touches the filesystem.
1011
+ */
1012
+ declare function renderIndex(input: RenderIndexInput): string;
1013
+
1014
+ /**
1015
+ * Walk a directory tree and return every subdirectory that contains at
1016
+ * least `minEntries` markdown files at its top level. The root directory
1017
+ * itself is included if it qualifies.
1018
+ *
1019
+ * "At its top level" means `.md` files **directly inside** the directory
1020
+ * (not in its subdirectories) — this is what an `_INDEX.md` in that
1021
+ * directory would index.
1022
+ *
1023
+ * `README.md` and `_INDEX.md` files are not counted (they are not
1024
+ * indexable entries). `.md` files starting with prefixes in `skipPrefixes`
1025
+ * are also not counted.
1026
+ *
1027
+ * Hidden directories (`.foo`) are not descended into. `_foo` directories
1028
+ * are descended into — they may legitimately contain content
1029
+ * (`_HUB-*.md`, `_INDEX.md` of subdirectories).
1030
+ *
1031
+ * A missing root directory returns an empty list rather than throwing.
1032
+ *
1033
+ * Use case — splitting a giant flat `_INDEX.md` into per-folder
1034
+ * indexes: scan a tree, find the meaningful directories, write an
1035
+ * `_INDEX.md` in each.
1036
+ */
1037
+ declare function findIndexableDirs(rootDir: string, options?: {
1038
+ minEntries?: number;
1039
+ skipPrefixes?: readonly string[];
1040
+ }): Promise<readonly string[]>;
1041
+
1042
+ //# sourceMappingURL=index.d.ts.map
1043
+
1044
+ type index_d$4_IndexEntry = IndexEntry;
1045
+ type index_d$4_RenderIndexInput = RenderIndexInput;
1046
+ type index_d$4_ScanOptions = ScanOptions;
1047
+ declare const index_d$4_findIndexableDirs: typeof findIndexableDirs;
1048
+ declare const index_d$4_renderIndex: typeof renderIndex;
1049
+ declare const index_d$4_scanDirectory: typeof scanDirectory;
1050
+ declare namespace index_d$4 {
1051
+ export { type index_d$4_IndexEntry as IndexEntry, type index_d$4_RenderIndexInput as RenderIndexInput, type index_d$4_ScanOptions as ScanOptions, index_d$4_findIndexableDirs as findIndexableDirs, index_d$4_renderIndex as renderIndex, index_d$4_scanDirectory as scanDirectory };
1052
+ }
1053
+
1054
+ /**
1055
+ * Frontmatter expected on a Runbook entry. `type` is the only required
1056
+ * field; the store tolerates absence of the rest and surfaces what is present.
1057
+ */
1058
+ interface RunbookFrontmatter {
1059
+ type: "runbook";
1060
+ status?: "active" | "draft" | "archived" | string;
1061
+ privacy?: "public" | "internal" | "personal" | string;
1062
+ /** ISO date (`YYYY-MM-DD`) when this runbook was last verified end-to-end. */
1063
+ last_tested?: string;
1064
+ /** Free-form incident category (e.g. `network-outage`, `mail-outage`). */
1065
+ "incident-type"?: string;
1066
+ created?: string;
1067
+ updated?: string;
1068
+ tags?: readonly string[];
1069
+ related?: readonly string[] | string;
1070
+ [key: string]: unknown;
1071
+ }
1072
+ /**
1073
+ * A parsed runbook entry.
1074
+ *
1075
+ * `slug` is the filename without the `.md` extension. Runbooks live in a
1076
+ * flat directory — no date-prefix convention, since the name is typically
1077
+ * descriptive (`service-restart-procedure`, `database-restore`).
1078
+ */
1079
+ interface RunbookEntry {
1080
+ readonly slug: string;
1081
+ readonly path: string;
1082
+ readonly frontmatter: RunbookFrontmatter;
1083
+ readonly body: string;
1084
+ }
1085
+ /** Filter criteria for {@link RunbookStore.filter}. */
1086
+ interface RunbookFilter {
1087
+ /** Match entries with `frontmatter.status` equal to this value. */
1088
+ status?: string;
1089
+ /** Match entries that contain this tag (case-sensitive). */
1090
+ tag?: string;
1091
+ /** Match entries with `frontmatter['incident-type']` equal to this value. */
1092
+ incidentType?: string;
1093
+ }
1094
+
1095
+ /**
1096
+ * Directory-backed runbook store. Expected layout is flat —
1097
+ * `<rootDir>/<slug>.md`, no subdirectories.
1098
+ *
1099
+ * Non-entry files at the root (README, _INDEX, anything not ending in `.md`)
1100
+ * are silently ignored. A missing root directory returns an empty list
1101
+ * rather than throwing.
1102
+ */
1103
+ declare class RunbookStore {
1104
+ readonly rootDir: string;
1105
+ constructor(rootDir: string);
1106
+ /** All entries, sorted by slug ascending. */
1107
+ list(): Promise<readonly RunbookEntry[]>;
1108
+ /** A single entry by slug, or undefined if not found. */
1109
+ get(slug: string): Promise<RunbookEntry | undefined>;
1110
+ /**
1111
+ * Subset of entries matching the given criteria. Empty/undefined fields
1112
+ * are ignored. Returns entries in the same order as {@link list}.
1113
+ */
1114
+ filter(criteria: RunbookFilter): Promise<readonly RunbookEntry[]>;
1115
+ /**
1116
+ * Entries whose `last_tested` is older than `staleAfterDays` from the
1117
+ * given reference date (default: today). Entries without a `last_tested`
1118
+ * field are treated as "never tested" and are always returned.
1119
+ *
1120
+ * This is the practical reason runbooks are a module rather than plain
1121
+ * data — periodic reverification is a real operational need.
1122
+ */
1123
+ getStaleByLastTested(staleAfterDays: number, referenceDate?: Date): Promise<readonly RunbookEntry[]>;
1124
+ }
1125
+
1126
+ //# sourceMappingURL=index.d.ts.map
1127
+
1128
+ type index_d$3_RunbookEntry = RunbookEntry;
1129
+ type index_d$3_RunbookFilter = RunbookFilter;
1130
+ type index_d$3_RunbookFrontmatter = RunbookFrontmatter;
1131
+ type index_d$3_RunbookStore = RunbookStore;
1132
+ declare const index_d$3_RunbookStore: typeof RunbookStore;
1133
+ declare namespace index_d$3 {
1134
+ export { type index_d$3_RunbookEntry as RunbookEntry, type index_d$3_RunbookFilter as RunbookFilter, type index_d$3_RunbookFrontmatter as RunbookFrontmatter, index_d$3_RunbookStore as RunbookStore };
1135
+ }
1136
+
1137
+ /**
1138
+ * A wiki link extracted from a markdown body.
1139
+ *
1140
+ * Examples (input → parsed shape):
1141
+ * `[[Foo]]` → { name: "Foo" }
1142
+ * `[[Foo|bar]]` → { name: "Foo", alias: "bar" }
1143
+ * `[[Foo#Section]]` → { name: "Foo", anchor: "Section" }
1144
+ * `[[Foo#Sec|bar]]` → { name: "Foo", anchor: "Sec", alias: "bar" }
1145
+ */
1146
+ interface WikiLink {
1147
+ /** Target page name as written, without the `.md` extension. */
1148
+ readonly name: string;
1149
+ /** Section anchor after `#`, if present. */
1150
+ readonly anchor?: string;
1151
+ /** Display alias after `|`, if present. */
1152
+ readonly alias?: string;
1153
+ /** Original raw text including `[[...]]` brackets. */
1154
+ readonly raw: string;
1155
+ }
1156
+ /**
1157
+ * A wiki link that did not resolve against the filesystem index.
1158
+ *
1159
+ * `sourcePath` is the absolute path of the file that contained the link.
1160
+ * `link` is the parsed link itself. Reason is a short string identifying
1161
+ * why resolution failed (e.g. `not-found`).
1162
+ */
1163
+ interface BrokenLink {
1164
+ readonly sourcePath: string;
1165
+ readonly link: WikiLink;
1166
+ readonly reason: "not-found" | "ambiguous";
1167
+ /** When `ambiguous`, the candidate paths that matched the name. */
1168
+ readonly candidates?: readonly string[];
1169
+ }
1170
+ /**
1171
+ * Aggregate result of {@link checkDirectory}.
1172
+ */
1173
+ interface LinkCheckResult {
1174
+ /** Number of `.md` files scanned. */
1175
+ readonly filesScanned: number;
1176
+ /** Total wiki links encountered across all files. */
1177
+ readonly totalLinks: number;
1178
+ /** Links that resolved to a unique target. */
1179
+ readonly resolved: number;
1180
+ /** Detail of unresolved links. */
1181
+ readonly broken: readonly BrokenLink[];
1182
+ /** Detail of ambiguous links (name matched multiple files). */
1183
+ readonly ambiguous: readonly BrokenLink[];
1184
+ }
1185
+
1186
+ /**
1187
+ * Extract every wiki link from a markdown body. Returns links in
1188
+ * document order; duplicates are preserved (callers can dedupe if they
1189
+ * want, but a broken link in two places is still two repairs).
1190
+ *
1191
+ * Code fences and inline code spans are not stripped — this module
1192
+ * treats markdown as text. Hosts that need to ignore links inside
1193
+ * code blocks should pre-filter the body.
1194
+ */
1195
+ declare function extractWikiLinks(body: string): readonly WikiLink[];
1196
+
1197
+ /**
1198
+ * Filesystem index of every `.md` file under a root.
1199
+ *
1200
+ * Provides two lookup paths:
1201
+ *
1202
+ * - `byBasename` — `name` (no `.md`) → list of absolute paths. Used for
1203
+ * bare wiki links like `[[Foo]]`, which Obsidian resolves by filename
1204
+ * anywhere in the vault.
1205
+ * - `byRelPath` — forward-slash relative path (no `.md`) → absolute path.
1206
+ * Used for path-aware wiki links like `[[Projects/Home/Foo]]` or
1207
+ * `[[../Foo]]`, where the operator wrote a path on purpose to
1208
+ * disambiguate.
1209
+ *
1210
+ * `rootDir` is retained so resolvers can interpret root-relative paths.
1211
+ */
1212
+ interface FileIndex {
1213
+ readonly byBasename: ReadonlyMap<string, readonly string[]>;
1214
+ readonly byRelPath: ReadonlyMap<string, string>;
1215
+ /**
1216
+ * Optional lowercase rel-path → absolute-path map for case-insensitive
1217
+ * fallback. Present only when {@link buildFileIndex} was called with
1218
+ * `caseInsensitive: true`. If two rel-paths collide after lowercasing,
1219
+ * the first walker-visited path wins; the conflict is silent (rare in
1220
+ * a well-organized tree).
1221
+ */
1222
+ readonly byRelPathLower?: ReadonlyMap<string, string>;
1223
+ readonly rootDir: string;
1224
+ }
1225
+ /**
1226
+ * Options for {@link buildFileIndex}.
1227
+ */
1228
+ interface BuildIndexOptions {
1229
+ /**
1230
+ * Build an additional lowercase `byRelPathLower` map so {@link resolveLink}
1231
+ * can fall back to case-insensitive matching when the exact-case path
1232
+ * lookup fails. Useful when the operator's wiki-link convention uses
1233
+ * different casing than the on-disk layout (a frequent situation when
1234
+ * content is migrated from one vault into another with renamed roots,
1235
+ * e.g. `[[Projects/Home/foo]]` vs `data/projects/home/foo.md`).
1236
+ *
1237
+ * Default: false. Bare-name lookup is unaffected — Obsidian historically
1238
+ * treats bare names case-sensitively, and we keep that.
1239
+ */
1240
+ caseInsensitive?: boolean;
1241
+ /**
1242
+ * Extra file extensions (besides `.md`) to include in the index. Each
1243
+ * extension must include the leading dot, e.g. `[".hwp", ".docx", ".pdf"]`.
1244
+ *
1245
+ * `.md` files are always indexed with the extension stripped from their
1246
+ * key — `[[Foo]]` matches `Foo.md`. Files with one of the additional
1247
+ * extensions are indexed with the extension *kept*, so they only match
1248
+ * when the operator writes the full filename — `[[Foo.hwp]]` matches
1249
+ * `Foo.hwp`. This avoids surprise collisions where `[[Foo]]` would
1250
+ * suddenly match a non-markdown file.
1251
+ *
1252
+ * Default: `[]` (only `.md` is indexed; preserves v1 behavior).
1253
+ *
1254
+ * Note: only `.md` files are scanned for wiki links by
1255
+ * {@link import("./checker.js").checkDirectory} and rewritten by
1256
+ * {@link import("./rewrite.js").rewriteDirectory}. Additional extensions
1257
+ * affect *resolution targets* (what a wiki link can point at), not
1258
+ * *scan sources* (what a wiki link can appear in).
1259
+ */
1260
+ additionalExtensions?: readonly string[];
1261
+ }
1262
+ /**
1263
+ * Walk `rootDir` recursively and build a {@link FileIndex} of every `.md`
1264
+ * file (plus any `additionalExtensions` requested).
1265
+ *
1266
+ * Hidden directories (`.foo`) are skipped. `_foo` directories ARE indexed —
1267
+ * Obsidian wiki links can target any markdown file regardless of underscore
1268
+ * prefix, so we mirror that. A missing root returns an empty index.
1269
+ */
1270
+ declare function buildFileIndex(rootDir: string, options?: BuildIndexOptions): Promise<FileIndex>;
1271
+ /**
1272
+ * Resolution outcome for a single wiki link.
1273
+ *
1274
+ * - `unique`: one and only one file matched.
1275
+ * - `not-found`: no file matched.
1276
+ * - `ambiguous`: multiple files share the basename (only possible for
1277
+ * bare-name links; path-aware links always resolve uniquely or fail).
1278
+ */
1279
+ type ResolveOutcome = {
1280
+ kind: "unique";
1281
+ path: string;
1282
+ } | {
1283
+ kind: "not-found";
1284
+ } | {
1285
+ kind: "ambiguous";
1286
+ candidates: readonly string[];
1287
+ };
1288
+ /**
1289
+ * Optional hints that turn on path-aware resolution.
1290
+ *
1291
+ * - `sourcePath` — absolute path of the file the link was found in.
1292
+ * Required to resolve `../foo`-style relative links.
1293
+ *
1294
+ * When neither hint is set, behavior collapses to bare-name lookup
1295
+ * (basename only), matching the original v1 semantics.
1296
+ */
1297
+ interface ResolveOpts {
1298
+ sourcePath?: string;
1299
+ }
1300
+ /**
1301
+ * Look up a wiki link in the index. Three cases:
1302
+ *
1303
+ * 1. **Bare name** (`[[Foo]]`) — basename lookup only. Returns `unique`
1304
+ * if exactly one `Foo.md` exists, `ambiguous` if multiple, `not-found`
1305
+ * otherwise.
1306
+ * 2. **Root-relative path** (`[[Projects/Foo]]`) — exact rel-path lookup
1307
+ * against the index. Always `unique` or `not-found`.
1308
+ * 3. **Source-relative path** (`[[../Foo]]`, `[[./Foo]]`) — resolved
1309
+ * against `dirname(sourcePath)` first, then looked up by rel-path.
1310
+ * Requires `opts.sourcePath`; without it, returns `not-found`.
1311
+ *
1312
+ * Trailing `.md` in the link name is stripped before matching.
1313
+ */
1314
+ declare function resolveLink(name: string, index: FileIndex, opts?: ResolveOpts): ResolveOutcome;
1315
+ /**
1316
+ * Reduce an absolute path to one relative to `rootDir`, using forward
1317
+ * slashes. Useful for reporting.
1318
+ */
1319
+ declare function toRel(path: string, rootDir: string): string;
1320
+
1321
+ /**
1322
+ * Options for {@link checkDirectory}.
1323
+ */
1324
+ interface CheckDirectoryOptions extends BuildIndexOptions {
1325
+ }
1326
+ /**
1327
+ * Scan every `.md` file under `rootDir`, extract wiki links, and
1328
+ * report which ones cannot be resolved (or resolve to multiple files).
1329
+ *
1330
+ * This is read-only — it does not modify any file. The function returns
1331
+ * a structured result so hosts can decide whether to log, fail a build,
1332
+ * propose rewrites, etc.
1333
+ *
1334
+ * Pass `caseInsensitive: true` when wiki-link casing may differ from
1335
+ * the on-disk path (e.g. content migrated from a vault with different
1336
+ * directory naming conventions).
1337
+ */
1338
+ declare function checkDirectory(rootDir: string, options?: CheckDirectoryOptions): Promise<LinkCheckResult>;
1339
+ /**
1340
+ * Group broken links by target name and return counts in descending order.
1341
+ * Useful for quickly identifying the most-cited missing pages.
1342
+ */
1343
+ declare function topBrokenTargets(broken: readonly BrokenLink[], limit?: number): readonly {
1344
+ readonly name: string;
1345
+ readonly count: number;
1346
+ }[];
1347
+
1348
+ /**
1349
+ * Map of wiki-link names to replace.
1350
+ *
1351
+ * Both keys and values are the `name` portion of a wiki link — what
1352
+ * appears between `[[` and the optional `#anchor` / `|alias`. Anchors
1353
+ * and aliases on the original link are preserved automatically.
1354
+ *
1355
+ * Examples:
1356
+ * { "API-Tokens": "secrets/api-tokens" }
1357
+ * [[API-Tokens]] → [[secrets/api-tokens]]
1358
+ * [[API-Tokens|토큰]] → [[secrets/api-tokens|토큰]]
1359
+ * [[API-Tokens#A|토큰]] → [[secrets/api-tokens#A|토큰]]
1360
+ *
1361
+ * Use the exact name string the operator wrote — no normalization is
1362
+ * applied. If the same broken target appears with different spellings
1363
+ * (e.g. case differences), provide one entry per spelling.
1364
+ */
1365
+ type RedirectionMap = ReadonlyMap<string, string>;
1366
+ /**
1367
+ * Options for {@link rewriteDirectory}.
1368
+ */
1369
+ interface RewriteOptions {
1370
+ /**
1371
+ * The redirection map driving the rewrite. Links whose name is not
1372
+ * a key in this map are left untouched.
1373
+ */
1374
+ redirections: RedirectionMap;
1375
+ /**
1376
+ * When true, do not write any files. The result still describes the
1377
+ * rewrites that would happen — useful for showing the operator what
1378
+ * a redirection map will do before it does it.
1379
+ */
1380
+ dryRun?: boolean;
1381
+ }
1382
+ /**
1383
+ * Per-file detail of a rewrite operation.
1384
+ */
1385
+ interface FileRewrite {
1386
+ readonly sourcePath: string;
1387
+ readonly rewrites: readonly {
1388
+ readonly from: string;
1389
+ readonly to: string;
1390
+ }[];
1391
+ }
1392
+ /**
1393
+ * Aggregate result of {@link rewriteDirectory}.
1394
+ */
1395
+ interface RewriteResult {
1396
+ readonly filesScanned: number;
1397
+ /** Files whose body was changed (or would be changed under dry-run). */
1398
+ readonly filesChanged: number;
1399
+ /** Total number of link replacements (may exceed `filesChanged`). */
1400
+ readonly rewritesApplied: number;
1401
+ /** Links matching a redirection key but ultimately not replaced — currently always 0; reserved for future skip reasons. */
1402
+ readonly skipped: number;
1403
+ readonly details: readonly FileRewrite[];
1404
+ readonly dryRun: boolean;
1405
+ }
1406
+ /**
1407
+ * Walk every `.md` file under `rootDir`, replace wiki links whose name
1408
+ * is a key in `redirections`, and write the changed files back to disk
1409
+ * (or describe what would change when `dryRun: true`).
1410
+ *
1411
+ * Only the link `name` is replaced. Anchors (`#Section`) and aliases
1412
+ * (`|display`) carry over verbatim onto the replacement link. The
1413
+ * surrounding markdown body is untouched.
1414
+ *
1415
+ * Rewriting is deterministic and order-independent — within a file,
1416
+ * every occurrence of every mapped key is replaced exactly once per
1417
+ * occurrence, with no chaining (the replacement is not re-scanned for
1418
+ * further matches).
1419
+ */
1420
+ declare function rewriteDirectory(rootDir: string, opts: RewriteOptions): Promise<RewriteResult>;
1421
+ /**
1422
+ * Pure transformation: take a markdown body, return the body with all
1423
+ * `[[…]]` links whose name appears in `redirections` rewritten. Exported
1424
+ * for testing and for callers that need to rewrite an in-memory string.
1425
+ *
1426
+ * The transformation does not touch markdown outside `[[…]]` patterns.
1427
+ */
1428
+ declare function rewriteBody(body: string, redirections: RedirectionMap): {
1429
+ newBody: string;
1430
+ fileRewrites: {
1431
+ from: string;
1432
+ to: string;
1433
+ }[];
1434
+ };
1435
+
1436
+ //# sourceMappingURL=index.d.ts.map
1437
+
1438
+ type index_d$2_BrokenLink = BrokenLink;
1439
+ type index_d$2_BuildIndexOptions = BuildIndexOptions;
1440
+ type index_d$2_CheckDirectoryOptions = CheckDirectoryOptions;
1441
+ type index_d$2_FileIndex = FileIndex;
1442
+ type index_d$2_FileRewrite = FileRewrite;
1443
+ type index_d$2_LinkCheckResult = LinkCheckResult;
1444
+ type index_d$2_RedirectionMap = RedirectionMap;
1445
+ type index_d$2_ResolveOpts = ResolveOpts;
1446
+ type index_d$2_ResolveOutcome = ResolveOutcome;
1447
+ type index_d$2_RewriteOptions = RewriteOptions;
1448
+ type index_d$2_RewriteResult = RewriteResult;
1449
+ type index_d$2_WikiLink = WikiLink;
1450
+ declare const index_d$2_buildFileIndex: typeof buildFileIndex;
1451
+ declare const index_d$2_checkDirectory: typeof checkDirectory;
1452
+ declare const index_d$2_extractWikiLinks: typeof extractWikiLinks;
1453
+ declare const index_d$2_resolveLink: typeof resolveLink;
1454
+ declare const index_d$2_rewriteBody: typeof rewriteBody;
1455
+ declare const index_d$2_rewriteDirectory: typeof rewriteDirectory;
1456
+ declare const index_d$2_toRel: typeof toRel;
1457
+ declare const index_d$2_topBrokenTargets: typeof topBrokenTargets;
1458
+ declare namespace index_d$2 {
1459
+ export { type index_d$2_BrokenLink as BrokenLink, type index_d$2_BuildIndexOptions as BuildIndexOptions, type index_d$2_CheckDirectoryOptions as CheckDirectoryOptions, type index_d$2_FileIndex as FileIndex, type index_d$2_FileRewrite as FileRewrite, type index_d$2_LinkCheckResult as LinkCheckResult, type index_d$2_RedirectionMap as RedirectionMap, type index_d$2_ResolveOpts as ResolveOpts, type index_d$2_ResolveOutcome as ResolveOutcome, type index_d$2_RewriteOptions as RewriteOptions, type index_d$2_RewriteResult as RewriteResult, type index_d$2_WikiLink as WikiLink, index_d$2_buildFileIndex as buildFileIndex, index_d$2_checkDirectory as checkDirectory, index_d$2_extractWikiLinks as extractWikiLinks, index_d$2_resolveLink as resolveLink, index_d$2_rewriteBody as rewriteBody, index_d$2_rewriteDirectory as rewriteDirectory, index_d$2_toRel as toRel, index_d$2_topBrokenTargets as topBrokenTargets };
1460
+ }
1461
+
1462
+ /**
1463
+ * Host-supplied LLM adapter. The proposer asks structured questions
1464
+ * (a prompt plus a JSON-shaped expected answer); the host runs the question
1465
+ * against whatever LLM facility it has — a sub-agent tool call (Claude Code),
1466
+ * an inline completion (Codex), an MCP call (Gemini), or a Claude Desktop
1467
+ * surface. The proposer does not care which.
1468
+ *
1469
+ * The interface is intentionally narrow: prompt in, structured answer out.
1470
+ * No streaming, no tool registration, no token budget knobs. Each
1471
+ * `LLMJudge.ask()` call is a single-shot decision query.
1472
+ *
1473
+ * This mirrors the `TranscriptAdapter` pattern in `@vortex-os/memory-extended`
1474
+ * — host-specific work is isolated to one adapter per host; the consumer
1475
+ * (`InsightProposer`, `HubProposer`) is host-agnostic.
1476
+ */
1477
+ interface LLMJudge {
1478
+ /**
1479
+ * Identifier of the host this adapter speaks to. Used for diagnostics and
1480
+ * for proposals to record which judge approved them (`accepted.log`).
1481
+ * Open-ended string so third-party hosts can register their own identifier.
1482
+ */
1483
+ readonly host: string;
1484
+ /**
1485
+ * Run a single-shot decision query against the host LLM.
1486
+ *
1487
+ * Implementations must:
1488
+ * - Pass `prompt` to the underlying LLM with whatever system instructions
1489
+ * the host normally uses (no special framing required from the caller).
1490
+ * - Constrain the response to match `expected.shape` — either by JSON-
1491
+ * mode generation, by structured output, or by a retry loop that
1492
+ * reformats the response. The proposer assumes the returned value
1493
+ * conforms to `expected.shape`.
1494
+ * - Throw on persistent format failure rather than returning malformed
1495
+ * data. Callers handle the rejection rather than the host masking it.
1496
+ *
1497
+ * The return type is `unknown` because the caller knows the shape it
1498
+ * expects (it supplied `expected.shape`) and runtime-validates after the
1499
+ * call. Keeping the static return loose avoids coupling the interface to
1500
+ * a particular validation library.
1501
+ */
1502
+ ask(prompt: string, expected: ExpectedShape): Promise<unknown>;
1503
+ }
1504
+ /**
1505
+ * Shape descriptor for the expected LLM response. Kept minimal so any
1506
+ * validation library (zod, valibot, hand-rolled) can produce one.
1507
+ *
1508
+ * - `shape: "string"` — a single free-form string answer (e.g. a yes/no,
1509
+ * a slug, a summary).
1510
+ * - `shape: "json"` — a JSON value matching the supplied JSON-Schema-like
1511
+ * descriptor. Implementations may pass the descriptor to a structured-
1512
+ * output mode if the underlying LLM supports it; otherwise they must
1513
+ * validate after generation.
1514
+ */
1515
+ type ExpectedShape = {
1516
+ readonly shape: "string";
1517
+ } | {
1518
+ readonly shape: "json";
1519
+ /**
1520
+ * Free-form schema descriptor — passed through to the host's structured
1521
+ * output mechanism if any, otherwise informational only. The proposer
1522
+ * still runs its own runtime check on the returned value, so this
1523
+ * field's contract is best-effort.
1524
+ */
1525
+ readonly schema: unknown;
1526
+ };
1527
+
1528
+ /**
1529
+ * The single contract that both proposer surfaces (insight capture, hub
1530
+ * auto-curation) implement. Generic over the input shape so each surface can
1531
+ * declare its own evaluation input — a recent-turns buffer for the insight
1532
+ * surface, a filesystem snapshot for the hub surface.
1533
+ *
1534
+ * `evaluate()` is intentionally pure: it inspects its input, optionally
1535
+ * consults the LLM, and returns either a {@link Proposal} or `null`. Side
1536
+ * effects (writing the captured insight, recording the decline) are
1537
+ * captured as thunks inside the returned Proposal and only run after the
1538
+ * user decides.
1539
+ *
1540
+ * A `null` return means "no proposal at this time" — the proposer's
1541
+ * thresholds were not met, the LLM declined, the input fingerprint was
1542
+ * already on the decline list, or nothing meaningful changed since the last
1543
+ * evaluation. The caller must not treat `null` as an error.
1544
+ */
1545
+ interface Proposer<Input, P extends Proposal = Proposal> {
1546
+ readonly kind: string;
1547
+ evaluate(input: Input, ctx: ProposerContext): Promise<P | null>;
1548
+ }
1549
+ /**
1550
+ * Shared evaluation context handed to every proposer call.
1551
+ *
1552
+ * - `cwd` is the instance's repository root — proposers resolve `data/`
1553
+ * paths relative to it. Absolute paths never leak into proposals (the
1554
+ * proposal's action carries data-relative paths).
1555
+ * - `declinedFingerprints` is pre-loaded by the orchestrator so the
1556
+ * proposer can do its decline check without touching the filesystem
1557
+ * during evaluation.
1558
+ * - `llm` is the host-supplied judge. Injected so the proposer stays
1559
+ * host-agnostic.
1560
+ * - `now` is injected (rather than `new Date()` inside the proposer) so
1561
+ * verify scenarios can pin time for deterministic fingerprints.
1562
+ */
1563
+ interface ProposerContext {
1564
+ readonly cwd: string;
1565
+ readonly declinedFingerprints: ReadonlySet<string>;
1566
+ readonly llm: LLMJudge;
1567
+ readonly now: Date;
1568
+ }
1569
+ /**
1570
+ * A single proposal handed to the host for the user to accept or decline.
1571
+ *
1572
+ * The proposal carries enough preview content that the user can decide
1573
+ * without further round-trips. `onAccept` / `onDecline` are thunks — the
1574
+ * side-effect logic was captured at evaluation time but does not run until
1575
+ * the user decides. This keeps `Proposer.evaluate()` pure.
1576
+ *
1577
+ * `fingerprint` identifies the proposal for decline-tracking. It must
1578
+ * include the chosen `action.kind` and target path so the same topic can
1579
+ * re-surface with a different placement decision if the LLM's call shifts
1580
+ * (e.g. user declined an `append-section` to file X, but a later evaluation
1581
+ * proposes `create-file` in a sibling folder — different fingerprint,
1582
+ * eligible to surface).
1583
+ */
1584
+ interface Proposal {
1585
+ readonly kind: string;
1586
+ readonly fingerprint: string;
1587
+ readonly action: ProposalAction;
1588
+ readonly preview: string;
1589
+ readonly rationale: string;
1590
+ readonly sourceRefs: readonly string[];
1591
+ readonly onAccept: () => Promise<AcceptResult>;
1592
+ readonly onDecline: () => Promise<void>;
1593
+ }
1594
+ /**
1595
+ * Placement action chosen by the proposer. Four kinds cover the full
1596
+ * lifecycle of topic-aware capture; the proposer biases toward the *least*
1597
+ * structural change consistent with the captured content.
1598
+ *
1599
+ * Preference order (extend before create):
1600
+ * `append-section` > `create-file` > `create-folder` > `update-file`
1601
+ *
1602
+ * - `create-folder` — topic is new; create the folder and seed the first
1603
+ * file inside it.
1604
+ * - `create-file` — topic folder exists; create a new sibling file. The
1605
+ * proposer chose this because the content does not belong in any
1606
+ * existing file in the folder.
1607
+ * - `append-section` — an existing file is the right home; append a new
1608
+ * `## <sectionHeader>` section to it. Use this for an ongoing topic
1609
+ * where the new content is an addition, not a rewrite.
1610
+ * - `update-file` — an existing file needs in-place revision (status
1611
+ * update, factual correction, summary refresh). Rare; prefer
1612
+ * `append-section` when the new content is additive.
1613
+ *
1614
+ * All paths are relative to `data/` so proposals never embed absolute paths
1615
+ * (proposals can be serialized into `_proactive-curator/` and re-evaluated
1616
+ * later without being tied to a particular workspace location).
1617
+ */
1618
+ type ProposalAction = {
1619
+ readonly kind: "create-folder";
1620
+ readonly folderPath: string;
1621
+ readonly filename: string;
1622
+ readonly body: string;
1623
+ } | {
1624
+ readonly kind: "create-file";
1625
+ readonly folderPath: string;
1626
+ readonly filename: string;
1627
+ readonly body: string;
1628
+ } | {
1629
+ readonly kind: "append-section";
1630
+ readonly filePath: string;
1631
+ readonly sectionHeader: string;
1632
+ readonly body: string;
1633
+ } | {
1634
+ readonly kind: "update-file";
1635
+ readonly filePath: string;
1636
+ readonly body: string;
1637
+ readonly reason: string;
1638
+ };
1639
+ /**
1640
+ * Result returned by {@link Proposal.onAccept}. The host uses this to
1641
+ * confirm the write succeeded and to surface the next-action hint to the
1642
+ * user in plain language.
1643
+ */
1644
+ interface AcceptResult {
1645
+ readonly writtenPath: string;
1646
+ readonly actionApplied: ProposalAction["kind"];
1647
+ readonly nextActionHint?: string;
1648
+ }
1649
+
1650
+ /**
1651
+ * Input to {@link computeFingerprint}. Two shapes — one per surface — share
1652
+ * a single function so the decline store can carry both kinds of
1653
+ * fingerprints in the same lookup set.
1654
+ *
1655
+ * The action-aware fingerprint is the load-bearing design choice: declining
1656
+ * an `append-section` proposal for topic X does *not* prevent a later
1657
+ * `create-file` proposal for the same topic from surfacing. The LLM's
1658
+ * placement decision is part of the fingerprint, so a shifted decision is
1659
+ * a new proposal.
1660
+ */
1661
+ type FingerprintInput = {
1662
+ readonly kind: "capture-insight";
1663
+ readonly turnIds: readonly string[];
1664
+ readonly topic: string;
1665
+ readonly actionKind: ProposalAction["kind"];
1666
+ readonly targetPath: string;
1667
+ } | {
1668
+ readonly kind: "create-hub";
1669
+ readonly topic: string;
1670
+ readonly sourceDocs: readonly string[];
1671
+ readonly actionKind: ProposalAction["kind"];
1672
+ readonly targetPath: string;
1673
+ };
1674
+ /**
1675
+ * Compute a stable 16-hex-char fingerprint for a proposal. Stable means:
1676
+ * identical logical input — regardless of source ordering of array fields or
1677
+ * path separator style — produces an identical fingerprint, so the decline
1678
+ * lookup is reliable across machines (work / home) and across path-separator
1679
+ * regimes (Windows backslash vs POSIX slash).
1680
+ *
1681
+ * SHA-256 truncated to 16 hex chars (64 bits) is enough collision resistance
1682
+ * for a per-instance decline set; the full hash adds storage with no
1683
+ * practical benefit at this scale.
1684
+ */
1685
+ declare function computeFingerprint(input: FingerprintInput): string;
1686
+
1687
+ type ProposalKind = "capture-insight" | "create-hub";
1688
+ interface DeclinedEntry {
1689
+ /** ISO-8601 timestamp of when the user declined. */
1690
+ readonly declinedAt: string;
1691
+ /** Topic slug at decline time (for the audit trail; not part of the fingerprint). */
1692
+ readonly topic: string;
1693
+ /** ISO-8601 timestamp after which this entry is eligible for re-evaluation. */
1694
+ readonly expiresAt: string;
1695
+ /** The action the user declined — `create-file`, `append-section`, etc. */
1696
+ readonly actionKind: ProposalAction["kind"];
1697
+ /** Target path the declined action would have written/touched (data-relative). */
1698
+ readonly targetPath: string;
1699
+ /** Hub proposals only — the source-doc cluster that triggered this proposal. */
1700
+ readonly sourceDocs?: readonly string[];
1701
+ }
1702
+ /**
1703
+ * Load the active (un-expired) declined fingerprints. The returned set is
1704
+ * what {@link ProposerContext.declinedFingerprints} carries during
1705
+ * `evaluate()` so the proposer can do its decline check without filesystem
1706
+ * access at evaluation time.
1707
+ *
1708
+ * Missing or corrupt store files return an empty set rather than throwing —
1709
+ * a first-run instance simply has nothing declined.
1710
+ */
1711
+ declare function loadDeclinedFingerprints(cwd: string, now: Date): Promise<ReadonlySet<string>>;
1712
+ /**
1713
+ * Persist a decline. Purges expired entries from disk in the same write so
1714
+ * the store does not grow unbounded.
1715
+ */
1716
+ declare function recordDecline(cwd: string, args: {
1717
+ readonly kind: ProposalKind;
1718
+ readonly fingerprint: string;
1719
+ readonly topic: string;
1720
+ readonly actionKind: ProposalAction["kind"];
1721
+ readonly targetPath: string;
1722
+ readonly sourceDocs?: readonly string[];
1723
+ readonly now: Date;
1724
+ readonly expiryDays?: number;
1725
+ }): Promise<void>;
1726
+ /**
1727
+ * Append an acceptance record to the audit trail. Newline-delimited JSON so
1728
+ * the file is grep-friendly and tail-friendly without a parser.
1729
+ */
1730
+ declare function recordAcceptance(cwd: string, args: {
1731
+ readonly kind: ProposalKind;
1732
+ readonly fingerprint: string;
1733
+ readonly topic: string;
1734
+ readonly actionKind: ProposalAction["kind"];
1735
+ readonly writtenPath: string;
1736
+ readonly now: Date;
1737
+ }): Promise<void>;
1738
+ /**
1739
+ * Clear declined entries. Optional `kind` argument scopes the reset to
1740
+ * just one surface. Called by `/curate --reset-declined` (sub-phase c).
1741
+ * Missing store file is a no-op — nothing to reset.
1742
+ */
1743
+ declare function resetDeclined(cwd: string, kind?: ProposalKind): Promise<void>;
1744
+
1745
+ interface InsightProposerOptions {
1746
+ /** Below this token count the proposer never asks the LLM. */
1747
+ readonly minTokensAccumulated: number;
1748
+ /** Below this turn count the proposer never asks the LLM. */
1749
+ readonly minTurnsBeforeFirstCheck: number;
1750
+ /**
1751
+ * Cap on how many topic folders to surface to the placement LLM call.
1752
+ * Large instance trees will exceed any practical LLM context budget;
1753
+ * truncation keeps the call tractable. Default 100.
1754
+ */
1755
+ readonly maxTreeEntries?: number;
1756
+ /** Days after which a decline becomes eligible for re-evaluation. */
1757
+ readonly declineExpiryDays?: number;
1758
+ }
1759
+ interface TurnRecord {
1760
+ readonly turnId: string;
1761
+ readonly role: "user" | "assistant" | "system";
1762
+ readonly content: string;
1763
+ }
1764
+ interface InsightInput {
1765
+ readonly recentTurns: readonly TurnRecord[];
1766
+ readonly accumulatedTokens: number;
1767
+ }
1768
+ interface InsightProposal extends Proposal {
1769
+ readonly kind: "capture-insight";
1770
+ }
1771
+ interface TopicFile {
1772
+ readonly path: string;
1773
+ readonly filename: string;
1774
+ readonly frontmatterTopic?: string;
1775
+ readonly tags?: readonly string[];
1776
+ }
1777
+ interface TopicFolderSnapshot {
1778
+ readonly path: string;
1779
+ readonly files: readonly TopicFile[];
1780
+ }
1781
+ interface TopicTreeSnapshot {
1782
+ readonly folders: readonly TopicFolderSnapshot[];
1783
+ /** True when the scan was truncated by `maxTreeEntries`. */
1784
+ readonly truncated: boolean;
1785
+ }
1786
+ declare class InsightProposer implements Proposer<InsightInput, InsightProposal> {
1787
+ private readonly options;
1788
+ readonly kind = "capture-insight";
1789
+ constructor(options: InsightProposerOptions);
1790
+ evaluate(input: InsightInput, ctx: ProposerContext): Promise<InsightProposal | null>;
1791
+ }
1792
+
1793
+ interface HubProposerOptions {
1794
+ /** Threshold for triggering propose-veto LLM gate. Below this, no proposal. Default 3. */
1795
+ readonly weakSignalAt: number;
1796
+ /** Threshold for switching to suppress-veto LLM gate. Default 5. */
1797
+ readonly strongSignalAt: number;
1798
+ /**
1799
+ * Content categories (data-relative) to scan for topic clusters. Default
1800
+ * `worklog`, `decision-log`, `runbooks`. Hub itself, `_memory/`, and the
1801
+ * curator's own `_proactive-curator/` are never scanned (a hub-of-hubs
1802
+ * is out of scope; memory is not topical content).
1803
+ */
1804
+ readonly scanCategories?: readonly string[];
1805
+ /** Decline expiry in days. Default 30. */
1806
+ readonly declineExpiryDays?: number;
1807
+ }
1808
+ interface HubInput {
1809
+ /**
1810
+ * Hub evaluation has no per-call input beyond context — the filesystem is
1811
+ * the input. This shape is kept for future extension (e.g. a hint to scan
1812
+ * only a specific category) without breaking the `Proposer<Input, P>`
1813
+ * contract.
1814
+ */
1815
+ readonly hint?: {
1816
+ readonly onlyCategories?: readonly string[];
1817
+ };
1818
+ }
1819
+ interface HubProposal extends Proposal {
1820
+ readonly kind: "create-hub";
1821
+ }
1822
+ declare class HubProposer implements Proposer<HubInput, HubProposal> {
1823
+ private readonly options;
1824
+ readonly kind = "create-hub";
1825
+ constructor(options: HubProposerOptions);
1826
+ evaluate(input: HubInput, ctx: ProposerContext): Promise<HubProposal | null>;
1827
+ }
1828
+
1829
+ /**
1830
+ * Sliding buffer of recent conversation turns. The host pushes each turn as
1831
+ * it occurs; the buffer caps at `maxTurns` and discards the oldest entries
1832
+ * past the cap. Token count is estimated cheaply so the host can poll
1833
+ * `tokenCount()` to decide when InsightProposer's threshold has been met.
1834
+ *
1835
+ * Default estimator is `chars / 4` — a rough baseline that overshoots for
1836
+ * CJK content and undershoots for ASCII code. Hosts that have a real
1837
+ * tokenizer can inject one via the `estimator` option.
1838
+ *
1839
+ * The buffer is process-local and not persisted — a new session starts
1840
+ * with an empty buffer, which is the intended UX (proactive proposals are
1841
+ * about *this conversation*, not a re-ingestion of past sessions; that is
1842
+ * what `memory-extended/consolidate` is for).
1843
+ */
1844
+ declare class TurnBuffer {
1845
+ private readonly turns;
1846
+ private readonly maxTurns;
1847
+ private readonly estimator;
1848
+ constructor(options?: {
1849
+ readonly maxTurns?: number;
1850
+ readonly estimator?: (turn: TurnRecord) => number;
1851
+ });
1852
+ /** Append a turn, evicting the oldest entry if the buffer is full. */
1853
+ add(turn: TurnRecord): void;
1854
+ /** Return the most recent `n` turns (or fewer if the buffer has fewer). */
1855
+ recent(n: number): readonly TurnRecord[];
1856
+ /** Number of turns currently buffered. */
1857
+ get length(): number;
1858
+ /** Estimated total token count over the entire buffer. */
1859
+ tokenCount(): number;
1860
+ /** Reset the buffer — typically called at session-end. */
1861
+ clear(): void;
1862
+ }
1863
+ /**
1864
+ * Backpressure controller for ambient mid-session triggers. When the user
1865
+ * declines several proposals in a row, the controller flips into
1866
+ * `active=true` and the host should pause ambient evaluations for the
1867
+ * remainder of the session. Manual `/curate` invocations are still
1868
+ * allowed — the controller exists only to suppress unsolicited prompts.
1869
+ *
1870
+ * Reset semantics:
1871
+ * - `recordAccept()` — any acceptance clears the streak.
1872
+ * - `recordIgnored()` — proposals the host surfaced but the user did not
1873
+ * explicitly accept/decline do not count toward the streak.
1874
+ * - `reset()` — explicit reset, used by `/curate` (the user re-engaging
1875
+ * after backpressure kicked in).
1876
+ */
1877
+ declare class AmbientBackpressure {
1878
+ private declineStreak;
1879
+ private readonly maxStreak;
1880
+ constructor(options?: {
1881
+ readonly maxStreak?: number;
1882
+ });
1883
+ /** Record a user decline. Counts toward backpressure. */
1884
+ recordDecline(): void;
1885
+ /** Record a user acceptance. Clears the streak. */
1886
+ recordAccept(): void;
1887
+ /** Explicit reset (e.g. user runs `/curate` after backpressure activated). */
1888
+ reset(): void;
1889
+ /** True when the streak has reached the maximum and ambient should pause. */
1890
+ isActive(): boolean;
1891
+ /** Current decline streak — exposed for diagnostics. */
1892
+ get streak(): number;
1893
+ }
1894
+
1895
+ /**
1896
+ * Minimal shape of a recall hit the ambient recaller consumes.
1897
+ *
1898
+ * Defined locally — `proactive-curator` does not depend on
1899
+ * `@vortex-os/memory-extended`. The host injects a {@link RecallFn} whose
1900
+ * result structurally satisfies this shape (memory-extended's `RecallHit`
1901
+ * carries all of these fields plus a few extra, so the wiring is
1902
+ * zero-friction). This mirrors how `LLMJudge` keeps the host's LLM facility
1903
+ * behind an injected adapter: the recall *engine* lives in another package;
1904
+ * the *decision of when to surface a hit in conversation* lives here.
1905
+ */
1906
+ interface AmbientRecallHit {
1907
+ readonly id: string;
1908
+ /** Cosine similarity to the query (1 = identical direction). */
1909
+ readonly score: number;
1910
+ readonly name: string;
1911
+ /** Short body excerpt for the host to weave into prose. */
1912
+ readonly excerpt: string;
1913
+ /** Corpus the hit came from (e.g. "memory", "session-archive"). */
1914
+ readonly source: string;
1915
+ readonly type: string;
1916
+ readonly updated: string | null;
1917
+ readonly tags: readonly string[];
1918
+ }
1919
+ /**
1920
+ * Host-injected recall function. The host wires its `memory-extended` recall
1921
+ * engine (sqlite + vector + embedder + session chunks) behind this single
1922
+ * call so the recaller stays engine-agnostic and `proactive-curator` stays
1923
+ * free of any `memory-extended` dependency.
1924
+ */
1925
+ interface RecallFn {
1926
+ (query: string, opts?: {
1927
+ readonly k?: number;
1928
+ }): Promise<{
1929
+ readonly hits: readonly AmbientRecallHit[];
1930
+ }>;
1931
+ }
1932
+ /** One surfaced suggestion — a recall hit that passed the ambient gate. */
1933
+ interface RecallSuggestion {
1934
+ readonly hit: AmbientRecallHit;
1935
+ /** Convenience copy of `hit.score` (the gate's ranking key). */
1936
+ readonly score: number;
1937
+ }
1938
+ /**
1939
+ * Result of one {@link AmbientRecaller.consider} call. Carries the surviving
1940
+ * suggestion(s) plus diagnostics (how many hits the engine returned, how many
1941
+ * each gate dropped) so an instance can tune `minScore` against real
1942
+ * conversations rather than guessing.
1943
+ */
1944
+ interface AmbientRecallResult {
1945
+ /** Hits that passed every gate, best-first, capped at `maxSuggestions`. */
1946
+ readonly suggestions: readonly RecallSuggestion[];
1947
+ /** True when backpressure is active — the recaller stayed silent. */
1948
+ readonly suppressed: boolean;
1949
+ /** The query actually used (derived or explicit); null if none/empty. */
1950
+ readonly query: string | null;
1951
+ /** Hits returned by the engine before filtering (transparency for tuning). */
1952
+ readonly considered: number;
1953
+ /** Hits dropped for scoring below `minScore`. */
1954
+ readonly belowThreshold: number;
1955
+ /** Hits dropped as already surfaced earlier this session. */
1956
+ readonly deduped: number;
1957
+ }
1958
+ interface AmbientRecallerOptions {
1959
+ readonly recall: RecallFn;
1960
+ /**
1961
+ * Minimum cosine score for a hit to be worth surfacing unprompted. The
1962
+ * single most important tuning knob — too low surfaces noise (naggy), too
1963
+ * high never fires. The right value depends on the embedder; calibrate per
1964
+ * instance against real conversations (the result diagnostics help). The
1965
+ * default 0.5 is a conservative starting point for the bundled e5-small
1966
+ * embedder.
1967
+ */
1968
+ readonly minScore?: number;
1969
+ /** Max suggestions per `consider()`. Default 1 — ambient is one nudge, not a list. */
1970
+ readonly maxSuggestions?: number;
1971
+ /** Below this query length, do not even call the engine. Default 12. */
1972
+ readonly minQueryChars?: number;
1973
+ /**
1974
+ * How many candidates to ask the engine for. Defaults to
1975
+ * `maxSuggestions + 4` so dedup/threshold filtering has headroom.
1976
+ */
1977
+ readonly candidateK?: number;
1978
+ /** Shared backpressure controller. Omit to use a fresh one. */
1979
+ readonly backpressure?: AmbientBackpressure;
1980
+ }
1981
+ /**
1982
+ * Decides *whether* and *which* past memory to surface in the flow of a live
1983
+ * conversation — the ambient counterpart to the explicit `/recall` command
1984
+ * (memory-extended-design.md deferred this "notice and offer" reliability to
1985
+ * a proactive-curator-connected follow-up; this is it).
1986
+ *
1987
+ * It does not embed, search, or format anything itself. It takes the recent
1988
+ * conversation, derives a query, asks the injected recall engine, and applies
1989
+ * three gates so the surface stays useful and non-naggy:
1990
+ *
1991
+ * 1. **Backpressure** — after N consecutive declines (see
1992
+ * {@link AmbientBackpressure}) it goes silent for the rest of the session.
1993
+ * 2. **Score threshold** — only confident hits surface.
1994
+ * 3. **Session dedup** — a hit already offered this session is not re-offered.
1995
+ *
1996
+ * Stateful by design (the dedup set + backpressure streak live across turns),
1997
+ * so a host keeps ONE instance alive for the session — a long-lived runtime
1998
+ * or the opt-in embedder daemon. A stateless per-call host (a plain slash
1999
+ * command) cannot carry the gate state; that is exactly why the default
2000
+ * Claude Code surface is agent-guidance, not a command (see the design docs).
2001
+ */
2002
+ declare class AmbientRecaller {
2003
+ private readonly recall;
2004
+ private readonly minScore;
2005
+ private readonly maxSuggestions;
2006
+ private readonly minQueryChars;
2007
+ private readonly candidateK;
2008
+ private readonly backpressure;
2009
+ private readonly surfaced;
2010
+ constructor(options: AmbientRecallerOptions);
2011
+ /**
2012
+ * Consider surfacing a memory given the current conversation. Pass an
2013
+ * explicit `query` (the host knows what the user is referencing) or recent
2014
+ * `turns` to derive one from. Returns the gated suggestions plus diagnostics.
2015
+ *
2016
+ * Does not catch errors from the injected engine — a failing embedder
2017
+ * should surface to the host (which decides whether to swallow it for the
2018
+ * turn), not be masked here.
2019
+ */
2020
+ consider(input: {
2021
+ readonly query?: string;
2022
+ readonly turns?: readonly TurnRecord[];
2023
+ }): Promise<AmbientRecallResult>;
2024
+ /** The user engaged with a surfaced suggestion — clears the decline streak. */
2025
+ recordAccept(): void;
2026
+ /** The user dismissed a surfaced suggestion — counts toward backpressure. */
2027
+ recordDecline(): void;
2028
+ /** True when backpressure has silenced ambient surfacing for the session. */
2029
+ get suppressed(): boolean;
2030
+ /**
2031
+ * Re-engage after backpressure (the user explicitly asks for suggestions
2032
+ * again, e.g. via `/curate`). Clears the decline streak AND the dedup set
2033
+ * so previously surfaced hits can be offered again.
2034
+ */
2035
+ reset(): void;
2036
+ }
2037
+ /**
2038
+ * Build a recall query from recent turns. Uses the most recent *user* turn —
2039
+ * in ambient use the trigger is the user's latest utterance ("didn't I do
2040
+ * this before?"), and that utterance is the query. Falls back to the most
2041
+ * recent turn of any role if the window has no user turn.
2042
+ */
2043
+ declare function deriveQueryFromTurns(turns: readonly TurnRecord[]): string;
2044
+
2045
+ /**
2046
+ * Shared building blocks for host `LLMJudge` adapters that work by injecting a
2047
+ * single-shot "prompt in, string out" call.
2048
+ *
2049
+ * Every first-party host (Claude Code, Codex, Gemini, Claude Desktop) reaches
2050
+ * its LLM differently — a sub-agent tool call, an inline completion, an MCP
2051
+ * call, a desktop surface — but that difference is entirely in the *raw
2052
+ * invoker* the host injects. The work *around* the call is identical: frame
2053
+ * the proposer's prompt so the model returns only the requested shape, then
2054
+ * parse the raw response tolerantly. That shared work lives here so each host
2055
+ * adapter is a thin shell over one injected function (the design's
2056
+ * "subsequent hosts inherit the pattern").
2057
+ */
2058
+ /** A host's single-shot LLM call: a prompt in, a raw string out. */
2059
+ interface InjectedInvoker {
2060
+ (input: {
2061
+ readonly prompt: string;
2062
+ }): Promise<string>;
2063
+ }
2064
+ /**
2065
+ * Base error for injected-host LLMJudge adapters. Each host subclass narrows
2066
+ * the `name` (e.g. `ClaudeCodeLLMJudgeError`) so callers can distinguish the
2067
+ * source host while still catching the family with `instanceof LLMJudgeError`.
2068
+ */
2069
+ declare class LLMJudgeError extends Error {
2070
+ readonly raw?: string | undefined;
2071
+ constructor(message: string, raw?: string | undefined);
2072
+ }
2073
+ /**
2074
+ * Frame a proposer prompt for a single-shot host LLM call. Host-agnostic —
2075
+ * the instruction constrains the response to the expected shape and names no
2076
+ * host facility, so every adapter shares it verbatim.
2077
+ */
2078
+ declare function frameForJudge(prompt: string, expected: ExpectedShape): string;
2079
+ /**
2080
+ * Parse a raw host response into the expected shape. Strings pass through
2081
+ * trimmed; JSON answers are extracted tolerantly (a leading/trailing ```json
2082
+ * fence, or a sentence wrapped around the object) and `JSON.parse`'d.
2083
+ * Persistent format failure throws via the injected `makeError` factory so
2084
+ * each adapter raises its own typed error rather than the base masking it.
2085
+ */
2086
+ declare function parseJudgeResponse(raw: string, expected: ExpectedShape, makeError: (message: string, raw?: string) => Error): unknown;
2087
+ /**
2088
+ * Shared base for host adapters built on an injected invoker. The base owns
2089
+ * framing + parsing; a subclass declares only the host id and (optionally)
2090
+ * overrides {@link makeError} to raise a host-named error type.
2091
+ */
2092
+ declare abstract class InjectedLLMJudge implements LLMJudge {
2093
+ protected readonly invoker: InjectedInvoker;
2094
+ abstract readonly host: string;
2095
+ constructor(invoker: InjectedInvoker);
2096
+ /** Host adapters override to raise a host-named error subclass. */
2097
+ protected makeError(message: string, raw?: string): Error;
2098
+ ask(prompt: string, expected: ExpectedShape): Promise<unknown>;
2099
+ }
2100
+
2101
+ /**
2102
+ * Claude Code host adapter for `LLMJudge`.
2103
+ *
2104
+ * Claude Code's natural surface for a single-shot decision query is a
2105
+ * **sub-agent tool call** — a separate, ephemeral agent invoked by the outer
2106
+ * session with a focused prompt, returning its single answer. The adapter
2107
+ * does not know how to invoke a sub-agent itself (that is Claude Code runtime
2108
+ * territory); the host injects an invoker callback at construction time, and
2109
+ * the shared {@link InjectedLLMJudge} base handles prompt framing + tolerant
2110
+ * response parsing on top of it.
2111
+ *
2112
+ * The invoker callback is intentionally minimal — a single async function
2113
+ * that takes a string and returns a string. Hosts that want to thread
2114
+ * additional metadata (session id, tool budget) can capture them in the
2115
+ * closure when they create the invoker.
2116
+ */
2117
+ /** Claude Code's sub-agent invoker — a `prompt in, string out` call. */
2118
+ type ClaudeCodeSubAgentInvoker = InjectedInvoker;
2119
+ declare class ClaudeCodeLLMJudgeError extends LLMJudgeError {
2120
+ constructor(message: string, raw?: string);
2121
+ }
2122
+ declare class ClaudeCodeLLMJudge extends InjectedLLMJudge {
2123
+ readonly host = "claude-code";
2124
+ protected makeError(message: string, raw?: string): Error;
2125
+ }
2126
+
2127
+ /**
2128
+ * Codex CLI host adapter for `LLMJudge`.
2129
+ *
2130
+ * Codex's natural surface for a single-shot decision query is an **inline
2131
+ * completion** — the host runs the framed prompt as a one-off completion and
2132
+ * returns the text. As with every adapter, the host injects that call; the
2133
+ * shared base supplies framing + parsing. The only host-specific surface here
2134
+ * is the `host` identifier (`"codex"`, matching the sessionArchive adapter
2135
+ * convention) and the typed error.
2136
+ */
2137
+ /** Codex's inline-completion invoker — a `prompt in, string out` call. */
2138
+ type CodexCompletionInvoker = InjectedInvoker;
2139
+ declare class CodexLLMJudgeError extends LLMJudgeError {
2140
+ constructor(message: string, raw?: string);
2141
+ }
2142
+ declare class CodexLLMJudge extends InjectedLLMJudge {
2143
+ readonly host = "codex";
2144
+ protected makeError(message: string, raw?: string): Error;
2145
+ }
2146
+
2147
+ /**
2148
+ * Gemini CLI host adapter for `LLMJudge`.
2149
+ *
2150
+ * Gemini's natural surface for a single-shot decision query is an **MCP call**
2151
+ * — the host routes the framed prompt through its model-call tool and returns
2152
+ * the text. The host injects that call; the shared base supplies framing +
2153
+ * parsing. Host-specific surface is the `host` identifier (`"gemini"`,
2154
+ * matching the sessionArchive adapter convention) and the typed error.
2155
+ */
2156
+ /** Gemini's MCP-call invoker — a `prompt in, string out` call. */
2157
+ type GeminiMcpInvoker = InjectedInvoker;
2158
+ declare class GeminiLLMJudgeError extends LLMJudgeError {
2159
+ constructor(message: string, raw?: string);
2160
+ }
2161
+ declare class GeminiLLMJudge extends InjectedLLMJudge {
2162
+ readonly host = "gemini";
2163
+ protected makeError(message: string, raw?: string): Error;
2164
+ }
2165
+
2166
+ /**
2167
+ * Claude Desktop host adapter for `LLMJudge`.
2168
+ *
2169
+ * Claude Desktop's natural surface for a single-shot decision query is a
2170
+ * **response on its own conversation surface** — the host poses the framed
2171
+ * prompt and returns the model's reply. The host injects that call; the
2172
+ * shared base supplies framing + parsing. Host-specific surface is the `host`
2173
+ * identifier (`"claude-desktop"`, matching the sessionArchive adapter
2174
+ * convention) and the typed error.
2175
+ */
2176
+ /** Claude Desktop's response invoker — a `prompt in, string out` call. */
2177
+ type ClaudeDesktopInvoker = InjectedInvoker;
2178
+ declare class ClaudeDesktopLLMJudgeError extends LLMJudgeError {
2179
+ constructor(message: string, raw?: string);
2180
+ }
2181
+ declare class ClaudeDesktopLLMJudge extends InjectedLLMJudge {
2182
+ readonly host = "claude-desktop";
2183
+ protected makeError(message: string, raw?: string): Error;
2184
+ }
2185
+
2186
+ //# sourceMappingURL=index.d.ts.map
2187
+
2188
+ type index_d$1_AcceptResult = AcceptResult;
2189
+ type index_d$1_AmbientBackpressure = AmbientBackpressure;
2190
+ declare const index_d$1_AmbientBackpressure: typeof AmbientBackpressure;
2191
+ type index_d$1_AmbientRecallHit = AmbientRecallHit;
2192
+ type index_d$1_AmbientRecallResult = AmbientRecallResult;
2193
+ type index_d$1_AmbientRecaller = AmbientRecaller;
2194
+ declare const index_d$1_AmbientRecaller: typeof AmbientRecaller;
2195
+ type index_d$1_AmbientRecallerOptions = AmbientRecallerOptions;
2196
+ type index_d$1_ClaudeCodeLLMJudge = ClaudeCodeLLMJudge;
2197
+ declare const index_d$1_ClaudeCodeLLMJudge: typeof ClaudeCodeLLMJudge;
2198
+ type index_d$1_ClaudeCodeLLMJudgeError = ClaudeCodeLLMJudgeError;
2199
+ declare const index_d$1_ClaudeCodeLLMJudgeError: typeof ClaudeCodeLLMJudgeError;
2200
+ type index_d$1_ClaudeCodeSubAgentInvoker = ClaudeCodeSubAgentInvoker;
2201
+ type index_d$1_ClaudeDesktopInvoker = ClaudeDesktopInvoker;
2202
+ type index_d$1_ClaudeDesktopLLMJudge = ClaudeDesktopLLMJudge;
2203
+ declare const index_d$1_ClaudeDesktopLLMJudge: typeof ClaudeDesktopLLMJudge;
2204
+ type index_d$1_ClaudeDesktopLLMJudgeError = ClaudeDesktopLLMJudgeError;
2205
+ declare const index_d$1_ClaudeDesktopLLMJudgeError: typeof ClaudeDesktopLLMJudgeError;
2206
+ type index_d$1_CodexCompletionInvoker = CodexCompletionInvoker;
2207
+ type index_d$1_CodexLLMJudge = CodexLLMJudge;
2208
+ declare const index_d$1_CodexLLMJudge: typeof CodexLLMJudge;
2209
+ type index_d$1_CodexLLMJudgeError = CodexLLMJudgeError;
2210
+ declare const index_d$1_CodexLLMJudgeError: typeof CodexLLMJudgeError;
2211
+ type index_d$1_DeclinedEntry = DeclinedEntry;
2212
+ type index_d$1_ExpectedShape = ExpectedShape;
2213
+ type index_d$1_FingerprintInput = FingerprintInput;
2214
+ type index_d$1_GeminiLLMJudge = GeminiLLMJudge;
2215
+ declare const index_d$1_GeminiLLMJudge: typeof GeminiLLMJudge;
2216
+ type index_d$1_GeminiLLMJudgeError = GeminiLLMJudgeError;
2217
+ declare const index_d$1_GeminiLLMJudgeError: typeof GeminiLLMJudgeError;
2218
+ type index_d$1_GeminiMcpInvoker = GeminiMcpInvoker;
2219
+ type index_d$1_HubInput = HubInput;
2220
+ type index_d$1_HubProposal = HubProposal;
2221
+ type index_d$1_HubProposer = HubProposer;
2222
+ declare const index_d$1_HubProposer: typeof HubProposer;
2223
+ type index_d$1_HubProposerOptions = HubProposerOptions;
2224
+ type index_d$1_InjectedInvoker = InjectedInvoker;
2225
+ type index_d$1_InjectedLLMJudge = InjectedLLMJudge;
2226
+ declare const index_d$1_InjectedLLMJudge: typeof InjectedLLMJudge;
2227
+ type index_d$1_InsightInput = InsightInput;
2228
+ type index_d$1_InsightProposal = InsightProposal;
2229
+ type index_d$1_InsightProposer = InsightProposer;
2230
+ declare const index_d$1_InsightProposer: typeof InsightProposer;
2231
+ type index_d$1_InsightProposerOptions = InsightProposerOptions;
2232
+ type index_d$1_LLMJudge = LLMJudge;
2233
+ type index_d$1_LLMJudgeError = LLMJudgeError;
2234
+ declare const index_d$1_LLMJudgeError: typeof LLMJudgeError;
2235
+ type index_d$1_Proposal = Proposal;
2236
+ type index_d$1_ProposalAction = ProposalAction;
2237
+ type index_d$1_ProposalKind = ProposalKind;
2238
+ type index_d$1_Proposer<Input, P extends Proposal = Proposal> = Proposer<Input, P>;
2239
+ type index_d$1_ProposerContext = ProposerContext;
2240
+ type index_d$1_RecallFn = RecallFn;
2241
+ type index_d$1_RecallSuggestion = RecallSuggestion;
2242
+ type index_d$1_TopicTreeSnapshot = TopicTreeSnapshot;
2243
+ type index_d$1_TurnBuffer = TurnBuffer;
2244
+ declare const index_d$1_TurnBuffer: typeof TurnBuffer;
2245
+ type index_d$1_TurnRecord = TurnRecord;
2246
+ declare const index_d$1_computeFingerprint: typeof computeFingerprint;
2247
+ declare const index_d$1_deriveQueryFromTurns: typeof deriveQueryFromTurns;
2248
+ declare const index_d$1_frameForJudge: typeof frameForJudge;
2249
+ declare const index_d$1_loadDeclinedFingerprints: typeof loadDeclinedFingerprints;
2250
+ declare const index_d$1_parseJudgeResponse: typeof parseJudgeResponse;
2251
+ declare const index_d$1_recordAcceptance: typeof recordAcceptance;
2252
+ declare const index_d$1_recordDecline: typeof recordDecline;
2253
+ declare const index_d$1_resetDeclined: typeof resetDeclined;
2254
+ declare namespace index_d$1 {
2255
+ export { type index_d$1_AcceptResult as AcceptResult, index_d$1_AmbientBackpressure as AmbientBackpressure, type index_d$1_AmbientRecallHit as AmbientRecallHit, type index_d$1_AmbientRecallResult as AmbientRecallResult, index_d$1_AmbientRecaller as AmbientRecaller, type index_d$1_AmbientRecallerOptions as AmbientRecallerOptions, index_d$1_ClaudeCodeLLMJudge as ClaudeCodeLLMJudge, index_d$1_ClaudeCodeLLMJudgeError as ClaudeCodeLLMJudgeError, type index_d$1_ClaudeCodeSubAgentInvoker as ClaudeCodeSubAgentInvoker, type index_d$1_ClaudeDesktopInvoker as ClaudeDesktopInvoker, index_d$1_ClaudeDesktopLLMJudge as ClaudeDesktopLLMJudge, index_d$1_ClaudeDesktopLLMJudgeError as ClaudeDesktopLLMJudgeError, type index_d$1_CodexCompletionInvoker as CodexCompletionInvoker, index_d$1_CodexLLMJudge as CodexLLMJudge, index_d$1_CodexLLMJudgeError as CodexLLMJudgeError, type index_d$1_DeclinedEntry as DeclinedEntry, type index_d$1_ExpectedShape as ExpectedShape, type index_d$1_FingerprintInput as FingerprintInput, index_d$1_GeminiLLMJudge as GeminiLLMJudge, index_d$1_GeminiLLMJudgeError as GeminiLLMJudgeError, type index_d$1_GeminiMcpInvoker as GeminiMcpInvoker, type index_d$1_HubInput as HubInput, type index_d$1_HubProposal as HubProposal, index_d$1_HubProposer as HubProposer, type index_d$1_HubProposerOptions as HubProposerOptions, type index_d$1_InjectedInvoker as InjectedInvoker, index_d$1_InjectedLLMJudge as InjectedLLMJudge, type index_d$1_InsightInput as InsightInput, type index_d$1_InsightProposal as InsightProposal, index_d$1_InsightProposer as InsightProposer, type index_d$1_InsightProposerOptions as InsightProposerOptions, type index_d$1_LLMJudge as LLMJudge, index_d$1_LLMJudgeError as LLMJudgeError, type index_d$1_Proposal as Proposal, type index_d$1_ProposalAction as ProposalAction, type index_d$1_ProposalKind as ProposalKind, type index_d$1_Proposer as Proposer, type index_d$1_ProposerContext as ProposerContext, type index_d$1_RecallFn as RecallFn, type index_d$1_RecallSuggestion as RecallSuggestion, type index_d$1_TopicTreeSnapshot as TopicTreeSnapshot, index_d$1_TurnBuffer as TurnBuffer, type index_d$1_TurnRecord as TurnRecord, index_d$1_computeFingerprint as computeFingerprint, index_d$1_deriveQueryFromTurns as deriveQueryFromTurns, index_d$1_frameForJudge as frameForJudge, index_d$1_loadDeclinedFingerprints as loadDeclinedFingerprints, index_d$1_parseJudgeResponse as parseJudgeResponse, index_d$1_recordAcceptance as recordAcceptance, index_d$1_recordDecline as recordDecline, index_d$1_resetDeclined as resetDeclined };
2256
+ }
2257
+
2258
+ /**
2259
+ * `/curate` — surface accumulated proposals from the proactive-curator
2260
+ * module. Two surfaces share one command:
2261
+ *
2262
+ * - `/curate` — run both proposers; surface any results.
2263
+ * - `/curate --insight` — InsightProposer only.
2264
+ * - `/curate --hub` — HubProposer only.
2265
+ * - `/curate --reset-declined` — clear the decline list (escape hatch).
2266
+ *
2267
+ * Host integration:
2268
+ * 1. Construct an `LLMJudge` adapter for the host (Claude Code / Codex /
2269
+ * Gemini / Claude Desktop) — see `@vortex-os/proactive-curator`.
2270
+ * 2. Optionally provide an `insightInputProvider` callback that returns
2271
+ * the current `InsightInput` (recent turns + accumulated tokens) when
2272
+ * the host wants the in-session insight surface. Without this provider
2273
+ * the insight proposer is skipped.
2274
+ * 3. Pass both to `createRitualRegistry({ llm, insightInputProvider })`.
2275
+ * The `/curate` command is registered only when an LLMJudge is
2276
+ * supplied — instances that have not wired up a host LLM get the rest
2277
+ * of the rituals without a half-broken curate command.
2278
+ *
2279
+ * Result shape carries the full `Proposal` objects (with `onAccept` and
2280
+ * `onDecline` thunks). Hosts render the previews, ask the user to decide,
2281
+ * then call the thunks. This is the one command in `session-rituals`
2282
+ * whose result is intentionally non-serializable — the thunks are the
2283
+ * point.
2284
+ */
2285
+ interface CurateOptions {
2286
+ readonly llm: LLMJudge;
2287
+ /**
2288
+ * Returns the current InsightInput when the host wants the in-session
2289
+ * surface to run. Absence is a feature: hosts that only want hub
2290
+ * curation can omit it and the insight proposer is skipped without
2291
+ * error.
2292
+ */
2293
+ readonly insightInputProvider?: () => InsightInput | null;
2294
+ readonly insightProposer?: InsightProposer;
2295
+ readonly hubProposer?: HubProposer;
2296
+ }
2297
+ type CurateAnyProposal = InsightProposal | HubProposal;
2298
+ interface CurateResult {
2299
+ readonly subcommand: "curate";
2300
+ readonly status: "ok" | "reset-declined";
2301
+ readonly proposals: readonly CurateAnyProposal[];
2302
+ readonly skipped: {
2303
+ readonly insight: boolean;
2304
+ readonly hub: boolean;
2305
+ };
2306
+ readonly nextActions: readonly string[];
2307
+ }
2308
+ /**
2309
+ * Build the `/curate` command bound to a particular LLMJudge and optional
2310
+ * input provider. Returned as a factory so the registry can decide at
2311
+ * registration time whether to install the command at all.
2312
+ */
2313
+ declare function curateCommand(options: CurateOptions): Command<CurateResult>;
2314
+
2315
+ /**
2316
+ * `/recall <query>` — hybrid semantic search over memories, defined in the
2317
+ * plugin over the `@vortex-os/memory-extended` engine (mirrors how
2318
+ * `/curate` is defined here over `proactive-curator`). Keeping the command
2319
+ * in the plugin lets the module stay free of any slash-commands dependency.
2320
+ *
2321
+ * Host integration:
2322
+ * 1. Supply an embedder. `vector.createLocalEmbedder()` is the bundled
2323
+ * default (local MiniLM, model downloads on first use); pass an
2324
+ * OpenAI/Voyage adapter to use an API instead.
2325
+ * 2. Pass it to `createRitualRegistry({ recall: { embed } })`. The command
2326
+ * is registered only when an embedder is supplied — instances that have
2327
+ * not wired one up get the rest of the rituals without a half-broken
2328
+ * recall command (same graceful-degradation rule as `/curate`).
2329
+ *
2330
+ * The handler returns the structured `RecallResult` (data, not a report —
2331
+ * operator decision 5 / 2026-05-29). The host renders a list with
2332
+ * `vector`/`recall` render helpers for an explicit search, or reads the
2333
+ * hits and phrases one in conversation for ambient use.
2334
+ */
2335
+ interface RecallOptions {
2336
+ /** Embedding function. `vector.createLocalEmbedder()` for the default. */
2337
+ readonly embed: vector.EmbedFn;
2338
+ /** Override the DB path. Default `<dataDir>/_indexes/memory.sqlite`. */
2339
+ readonly dbPath?: (ctx: ModuleContext) => string;
2340
+ /** Default hit count when `--k` is absent. Default 5. */
2341
+ readonly defaultK?: number;
2342
+ }
2343
+ declare function recallCommand(options: RecallOptions): Command<recall.RecallResult>;
2344
+
2345
+ /**
2346
+ * Options accepted by {@link createRitualRegistry}. All fields are optional;
2347
+ * each one unlocks an additional command surface when supplied:
2348
+ *
2349
+ * - `curate` — when an `LLMJudge` is provided, the `/curate` command from
2350
+ * `@vortex-os/proactive-curator` is registered. Without it the rest of
2351
+ * the rituals work as before and `/curate` is simply absent (graceful
2352
+ * degradation rather than a half-broken command).
2353
+ * - `recall` — when an embedder is provided, the `/recall` command over
2354
+ * `@vortex-os/memory-extended` is registered. Same graceful-degradation
2355
+ * rule: absent the embedder, semantic recall would not work, so the
2356
+ * command is simply not installed.
2357
+ */
2358
+ interface RitualRegistryOptions {
2359
+ readonly curate?: CurateOptions;
2360
+ readonly recall?: RecallOptions;
2361
+ }
2362
+ /**
2363
+ * Build a {@link CommandRegistry} with all ritual commands registered.
2364
+ *
2365
+ * Hosts that want a subset can register the individual exports instead.
2366
+ * This factory exists for the common case — "give me everything" — plus
2367
+ * opt-in surfaces that depend on host-supplied adapters (currently
2368
+ * `/curate`, which needs an `LLMJudge`).
2369
+ */
2370
+ declare function createRitualRegistry(options?: RitualRegistryOptions): CommandRegistry;
2371
+
2372
+ /**
2373
+ * Bridge the `@vortex-os/memory-extended` recall engine into a
2374
+ * `@vortex-os/proactive-curator` {@link AmbientRecaller} — defined in the
2375
+ * plugin, exactly where `/recall` and `/curate` are defined over their
2376
+ * respective engines. This keeps both modules free of a dependency on each
2377
+ * other; the plugin is the only place that knows about both.
2378
+ *
2379
+ * This is deliberately **not** a slash command. Ambient recall is stateful
2380
+ * across turns (its dedup set + backpressure streak), so it belongs to a
2381
+ * long-lived host — the opt-in embedder daemon / `UserPromptSubmit` hook, or
2382
+ * any host runtime that keeps one instance alive for the session. The default
2383
+ * Claude Code surface is agent-guidance (see `docs/proactive-curator-design.md`);
2384
+ * this factory is for hosts that opt into the *automatic* surface.
2385
+ *
2386
+ * Construct **once per session** and reuse the returned recaller so its gate
2387
+ * state persists. The injected `RecallFn` opens DB handles lazily per call
2388
+ * and closes them in a `finally`, so a long-lived process never holds a file
2389
+ * lock between turns. The embedder (the expensive, warm-able part) is
2390
+ * supplied by the host so it can keep the model resident across calls.
2391
+ */
2392
+ interface AmbientRecallFactoryOptions {
2393
+ /** Embedding function. The host keeps this warm (daemon) to avoid per-call model loads. */
2394
+ readonly embed: vector.EmbedFn;
2395
+ /** Override the DB path. Default `<dataDir>/_indexes/memory.sqlite`. */
2396
+ readonly dbPath?: (ctx: ModuleContext) => string;
2397
+ /** Restrict recall to one corpus. Omit to search all. */
2398
+ readonly source?: vector.VectorSource;
2399
+ /** Forwarded to AmbientRecaller — minimum cosine score to surface. Default 0.5. */
2400
+ readonly minScore?: number;
2401
+ /** Forwarded to AmbientRecaller — max suggestions per consider(). Default 1. */
2402
+ readonly maxSuggestions?: number;
2403
+ /** Forwarded to AmbientRecaller — skip the engine below this query length. Default 12. */
2404
+ readonly minQueryChars?: number;
2405
+ }
2406
+ /**
2407
+ * Build a session-scoped {@link AmbientRecaller} wired to the recall engine.
2408
+ * Returns `undefined`-free — the caller owns the lifecycle.
2409
+ */
2410
+ declare function createAmbientRecaller(ctx: ModuleContext, options: AmbientRecallFactoryOptions): AmbientRecaller;
2411
+
2412
+ /**
2413
+ * Output of {@link sessionStartCommand}'s handler. Pure data — the host
2414
+ * decides how to display it. Returning structured output rather than
2415
+ * printing keeps the command usable from non-CLI hosts (tests, web UIs).
2416
+ */
2417
+ interface SessionStartReport {
2418
+ readonly time: string;
2419
+ readonly repoRoot: string;
2420
+ readonly dataDir: string;
2421
+ readonly counts: Readonly<Record<string, number>>;
2422
+ readonly missing: readonly string[];
2423
+ }
2424
+ /**
2425
+ * `/session-start` — Emit a small report that anchors a new session.
2426
+ * No side effects: reads filesystem counts only.
2427
+ *
2428
+ * The host (e.g. Claude Code session loop) is expected to display the
2429
+ * report and decide what to do next. This command's job is to surface
2430
+ * the facts, not to take action.
2431
+ */
2432
+ declare const sessionStartCommand: Command<SessionStartReport>;
2433
+
2434
+ interface RecentWorklog {
2435
+ /** Path relative to the data directory (e.g. `worklog/2026/05/2026-05-30-foo.md`). */
2436
+ readonly path: string;
2437
+ /** First `# ` heading, else the filename without extension. */
2438
+ readonly title: string;
2439
+ }
2440
+ /** Outcome of the optional `git pull` the hook runs before reporting. */
2441
+ interface GitPullResult {
2442
+ readonly ran: boolean;
2443
+ readonly summary: string;
2444
+ /** True when the pull could not fast-forward (divergence / dirty tree). Never auto-resolved. */
2445
+ readonly conflict: boolean;
2446
+ }
2447
+ interface SessionStartHookReport {
2448
+ readonly time: string;
2449
+ readonly repoRoot: string;
2450
+ readonly dataDir: string;
2451
+ readonly counts: Readonly<Record<string, number>>;
2452
+ readonly missing: readonly string[];
2453
+ readonly recentWorklog: RecentWorklog | null;
2454
+ /** Worklog dates (`YYYY-MM-DD`) present within the gap window — for backfill detection. */
2455
+ readonly recentWorklogDates: readonly string[];
2456
+ /** Resolved environment label (e.g. home/work), or null when none is configured. */
2457
+ readonly environment: string | null;
2458
+ }
2459
+ /**
2460
+ * Gather the read-only facts for a start-of-session report: data-dir counts,
2461
+ * the most recent worklog, recent worklog dates, and an optional environment
2462
+ * label. Mirrors the `/session-start` command's counting.
2463
+ */
2464
+ declare function collectSessionStartReport(ctx: ModuleContext, opts?: {
2465
+ readonly now?: Date;
2466
+ readonly environment?: string | null;
2467
+ readonly gapWindowDays?: number;
2468
+ }): Promise<SessionStartHookReport>;
2469
+ /**
2470
+ * Days that have commits but no worklog — backfill candidates. Pure set
2471
+ * difference (`commitDays` − `presentDates`), de-duplicated and sorted. The
2472
+ * hook supplies `commitDays` from git; the report supplies the present dates.
2473
+ */
2474
+ declare function detectWorklogGaps(commitDays: readonly string[], presentDates: readonly string[]): string[];
2475
+ /**
2476
+ * Render a session-start report as a compact markdown block for a host hook
2477
+ * to inject as session context. A pull conflict and any worklog gaps are
2478
+ * surfaced as warnings (the agent acts on the gaps — see AGENT.md).
2479
+ */
2480
+ declare function renderSessionStartReport(report: SessionStartHookReport, extras?: {
2481
+ readonly git?: GitPullResult | null;
2482
+ readonly missingWorklogDays?: readonly string[];
2483
+ readonly catchUp?: {
2484
+ readonly ingestedLocal: number;
2485
+ readonly indexedPulled: number;
2486
+ readonly errors: number;
2487
+ };
2488
+ }): string;
2489
+
2490
+ /**
2491
+ * Ensure a dated worklog entry exists — the host-side **create** primitive
2492
+ * the worklog module deliberately leaves out (`WorklogStore` reads; `/log`
2493
+ * only appends and errors when today's entry is missing). Auto-worklog
2494
+ * (the agent at wind-down, or the SessionEnd net) needs *creation*, so it
2495
+ * lives here, where instance frontmatter conventions belong.
2496
+ *
2497
+ * Idempotent: if a worklog already exists for the date (with any keyword), it
2498
+ * is returned untouched (`created: false`) — never overwritten. Pair with
2499
+ * `appendSection` from `@vortex-os/worklog` to add the session's content.
2500
+ */
2501
+ interface EnsureWorklogResult {
2502
+ readonly path: string;
2503
+ readonly date: string;
2504
+ readonly keyword: string;
2505
+ /** True when this call created the file; false when one already existed. */
2506
+ readonly created: boolean;
2507
+ }
2508
+ declare function ensureWorklogEntry(ctx: ModuleContext, opts?: {
2509
+ readonly now?: Date;
2510
+ readonly keyword?: string;
2511
+ readonly title?: string;
2512
+ readonly body?: string;
2513
+ }): Promise<EnsureWorklogResult>;
2514
+
2515
+ /**
2516
+ * Start-of-session "catch-up": fold conversation transcripts into the local
2517
+ * search archive without the user ever having to wrap up a session.
2518
+ *
2519
+ * Two sources, one pass:
2520
+ * - **local (a)** — this machine's own transcripts that are not archived yet,
2521
+ * read from the agent's transcript store and scoped to the current project.
2522
+ * - **pulled (b)** — transcripts created on another machine that arrived as
2523
+ * normalized text via git sync. Their text is present but this machine's
2524
+ * DB (local, derived, gitignored) has never indexed them.
2525
+ *
2526
+ * Text only — vectorization is deferred to recall/rebuild so session start
2527
+ * stays fast. The whole step is gated by `autoRecord.archive` at the call site
2528
+ * and is best-effort: callers should treat a thrown archive backend (e.g. the
2529
+ * native sqlite module not built) as "skip", never as a fatal start error.
2530
+ */
2531
+ interface CatchUpResult {
2532
+ /** Local transcripts newly archived this run (source a). */
2533
+ readonly ingestedLocal: number;
2534
+ /** Normalized transcripts from another machine newly indexed (source b). */
2535
+ readonly indexedPulled: number;
2536
+ /** Per-session ingest errors (source a). */
2537
+ readonly errors: number;
2538
+ }
2539
+ interface CatchUpOptions {
2540
+ /** Restrict local ingest to one project's transcripts. Default: `ctx.repoRoot`. */
2541
+ readonly cwd?: string;
2542
+ /**
2543
+ * Transcript adapters for local ingest. Default: Claude Code only. Other
2544
+ * hosts (Codex, Gemini) can be added by a caller; tests inject fakes so the
2545
+ * scan never touches the real home directory.
2546
+ */
2547
+ readonly adapters?: sessionArchive.IngestParams["adapters"];
2548
+ /** Adapter environment override (e.g. a sandbox HOME). Tests use this. */
2549
+ readonly env?: sessionArchive.IngestParams["env"];
2550
+ }
2551
+ declare function catchUpSessions(ctx: ModuleContext, opts?: CatchUpOptions): Promise<CatchUpResult>;
2552
+
2553
+ /**
2554
+ * Hook wiring for `/vortex init`: make sure the instance's
2555
+ * `.claude/settings.json` registers the VortEX SessionStart / SessionEnd hooks,
2556
+ * so the boot report + worklog-net fire automatically without the user knowing
2557
+ * any command. NON-DESTRUCTIVE — like the MCP install merge, this preserves
2558
+ * every other hook and top-level field and only adds our two entries if absent.
2559
+ *
2560
+ * Pure functions here (parse / merge / detect); the command does the actual
2561
+ * file read/write around them. Keeping them pure makes the merge unit-testable
2562
+ * and the "writes only what's missing" guarantee verifiable.
2563
+ */
2564
+ declare const SESSION_START_COMMAND = "node plugins/session-rituals/scripts/session-start-hook.mjs";
2565
+ declare const SESSION_END_COMMAND = "node plugins/session-rituals/scripts/session-end-hook.mjs";
2566
+ interface HookCommand {
2567
+ readonly type: "command";
2568
+ readonly command: string;
2569
+ }
2570
+ interface HookGroup {
2571
+ readonly hooks: readonly HookCommand[];
2572
+ readonly matcher?: string;
2573
+ }
2574
+ interface ClaudeSettings {
2575
+ hooks?: {
2576
+ SessionStart?: HookGroup[];
2577
+ SessionEnd?: HookGroup[];
2578
+ [event: string]: HookGroup[] | undefined;
2579
+ };
2580
+ [key: string]: unknown;
2581
+ }
2582
+ interface EnsureHooksResult {
2583
+ readonly settings: ClaudeSettings;
2584
+ /** Which hook events we added a VortEX entry to (empty if already wired). */
2585
+ readonly added: readonly ("SessionStart" | "SessionEnd")[];
2586
+ /** True when nothing changed — every VortEX hook was already present. */
2587
+ readonly alreadyWired: boolean;
2588
+ }
2589
+ /**
2590
+ * Parse existing settings.json text. Empty/whitespace → `{}` (fresh). Throws on
2591
+ * malformed JSON so the caller aborts rather than clobbering a hand-edited file.
2592
+ */
2593
+ declare function parseSettings(text: string | null | undefined): ClaudeSettings;
2594
+ /**
2595
+ * Merge the VortEX hooks into an existing settings object WITHOUT mutating the
2596
+ * input. A hook event is left untouched if it already references our command
2597
+ * (idempotent); otherwise our group is appended alongside any existing groups.
2598
+ */
2599
+ declare function ensureVortexHooks(existing: ClaudeSettings | null | undefined): EnsureHooksResult;
2600
+ /** Serialize settings the way Claude writes them (2-space, trailing newline). */
2601
+ declare function serializeSettings(settings: ClaudeSettings): string;
2602
+
2603
+ interface ReindexResult {
2604
+ readonly dir: string;
2605
+ readonly status: "written" | "unchanged" | "missing";
2606
+ readonly entries: number;
2607
+ readonly bytes: number;
2608
+ }
2609
+ /**
2610
+ * `/reindex [dir]` — Regenerate `_INDEX.md` for one or all known data
2611
+ * directories. When called with no argument, processes all targets;
2612
+ * when called with a directory name, processes only that target.
2613
+ *
2614
+ * Returns a list of results so the host can summarize. Unchanged
2615
+ * indexes are reported as `unchanged` (no write performed); missing
2616
+ * directories are reported as `missing` (no write attempted).
2617
+ */
2618
+ declare const reindexCommand: Command<readonly ReindexResult[]>;
2619
+
2620
+ interface NewDecisionResult {
2621
+ readonly path: string;
2622
+ readonly date: string;
2623
+ readonly slug: string;
2624
+ }
2625
+ /**
2626
+ * `/decision <slug> <title...>` — Create a new Decision Log entry from
2627
+ * the canonical template at `data/decision-log/<today>-<slug>.md`.
2628
+ *
2629
+ * `slug` is required and used in the filename. The remaining tokens form
2630
+ * the entry title (the H1 in the rendered body). The command refuses to
2631
+ * overwrite an existing file — it errors out so the caller can decide.
2632
+ */
2633
+ declare const decisionCommand: Command<NewDecisionResult>;
2634
+
2635
+ interface WorklogAppendResult {
2636
+ readonly path: string;
2637
+ readonly date: string;
2638
+ readonly keyword: string;
2639
+ readonly sectionTitle: string;
2640
+ }
2641
+ /**
2642
+ * `/log <section-title>` — Append a `## <section-title>` section to today's
2643
+ * worklog entry. If no worklog exists for today, the command errors (entry
2644
+ * creation with frontmatter is left to the host so it can apply
2645
+ * project-specific conventions). The body of the new section is left empty
2646
+ * for the caller to fill in.
2647
+ *
2648
+ * This is a deliberately small command — the goal is to make "add a quick
2649
+ * note to today's worklog" a one-liner, not to replace a real editor.
2650
+ */
2651
+ declare const logCommand: Command<WorklogAppendResult>;
2652
+
2653
+ /**
2654
+ * "What should I do today?" — a read-only synthesis over existing records
2655
+ * (worklog + decision-log + open `- [ ]` checkboxes), the P2 work-management
2656
+ * surface. No new store: it reads what's already written and answers
2657
+ * "what were you doing, what's open, what's next" from it.
2658
+ *
2659
+ * Design goal (operator, 2026-05-30): adapt to EVERY data state — a brand-new
2660
+ * user with nothing, a partly-used instance, and a rich history must each get a
2661
+ * sensible report. So the collector returns a structured, presence-flagged
2662
+ * shape and the renderer branches on what's actually there rather than assuming
2663
+ * any section exists.
2664
+ */
2665
+ /** An unchecked `- [ ]` item lifted from a recent worklog body. */
2666
+ interface OpenTask {
2667
+ readonly text: string;
2668
+ /** Worklog date the task was found in (`YYYY-MM-DD`). */
2669
+ readonly fromDate: string;
2670
+ }
2671
+ interface OpenDecision {
2672
+ readonly title: string;
2673
+ readonly date: string;
2674
+ readonly slug: string;
2675
+ }
2676
+ interface AgendaReport {
2677
+ /** Most recent worklog (what you were last doing), or null if none. */
2678
+ readonly lastWorklog: {
2679
+ readonly date: string;
2680
+ readonly title: string;
2681
+ readonly path: string;
2682
+ } | null;
2683
+ /**
2684
+ * "Next up" lines lifted from the most recent worklog's hand-off section
2685
+ * (`## 다음 작업` / `## Next` / a `📋`-marked heading) — the planned-work
2686
+ * pointer, mirroring the vault TIL hand-off block. Empty if none.
2687
+ */
2688
+ readonly nextUp: readonly string[];
2689
+ /** Date of the worklog the nextUp lines came from (or null). */
2690
+ readonly nextUpFrom: string | null;
2691
+ /** Unchecked checkboxes from recent worklogs, newest first, capped. */
2692
+ readonly openTasks: readonly OpenTask[];
2693
+ /** Active (non-archived) decisions, newest first, capped. */
2694
+ readonly openDecisions: readonly OpenDecision[];
2695
+ /** Total worklog / decision counts — used to distinguish "new" from "quiet". */
2696
+ readonly worklogCount: number;
2697
+ readonly decisionCount: number;
2698
+ /** True when there is essentially nothing yet (brand-new instance). */
2699
+ readonly isEmpty: boolean;
2700
+ /** True when there are records but nothing actionable surfaced. */
2701
+ readonly nothingOpen: boolean;
2702
+ }
2703
+ interface CollectAgendaOptions {
2704
+ /** How many recent worklogs to scan for open tasks. Default 7. */
2705
+ readonly recentWorklogs?: number;
2706
+ /** Max open tasks / decisions to surface. Default 8 each. */
2707
+ readonly maxTasks?: number;
2708
+ readonly maxDecisions?: number;
2709
+ }
2710
+ /**
2711
+ * Pull unchecked GitHub-style checkboxes (`- [ ]` / `* [ ]`, any indent) from a
2712
+ * worklog body. Checked items (`- [x]`) are ignored. Returns the trimmed label.
2713
+ */
2714
+ /**
2715
+ * Lift the "next up / hand-off" section from a worklog body — the planned-work
2716
+ * pointer the agent leaves at wind-down (mirrors the vault's TIL `## 📋 다음
2717
+ * 작업` block). Matches a heading whose text contains any of: "다음 작업",
2718
+ * "다음 세션", "next", "next up", "todo", "후속", "📋". Returns the section's
2719
+ * content lines (bullets de-marked, blanks dropped), capped. Empty if no such
2720
+ * section. Stops at the next heading of the same-or-higher level.
2721
+ */
2722
+ declare function extractNextUp(body: string, max?: number): string[];
2723
+ /**
2724
+ * Pull unchecked GitHub-style checkboxes (`- [ ]` / `* [ ]`, any indent) from a
2725
+ * worklog body. Checked items (`- [x]`) are ignored. Returns the trimmed label.
2726
+ */
2727
+ declare function extractOpenTasks(body: string): string[];
2728
+ /**
2729
+ * Collect the agenda inputs. Read-only; tolerant of missing dirs (both stores
2730
+ * return empty rather than throwing), so a brand-new instance yields an empty —
2731
+ * but well-formed — report.
2732
+ */
2733
+ declare function collectAgenda(ctx: ModuleContext, opts?: CollectAgendaOptions): Promise<AgendaReport>;
2734
+ /**
2735
+ * Render the agenda as a compact markdown block. Branches on data state so the
2736
+ * output is sensible whether the instance is brand-new, quiet, or busy:
2737
+ * - brand-new (no records) → a short "getting started" nudge
2738
+ * - records but nothing open → "you're clear" + last activity
2739
+ * - open tasks / decisions → an actionable list
2740
+ */
2741
+ declare function renderAgenda(report: AgendaReport): string;
2742
+
2743
+ /**
2744
+ * `/agenda` — "What should I do today?" Read-only synthesis over existing
2745
+ * worklog + decision-log records (open `- [ ]` tasks, active decisions, last
2746
+ * activity). Returns structured {@link AgendaReport} data; the host renders it
2747
+ * (or calls `renderAgenda`). No writes, no new store — answers from what's
2748
+ * already recorded, and adapts to any data state (new / quiet / busy).
2749
+ */
2750
+ declare const agendaCommand: Command<AgendaReport>;
2751
+
2752
+ /**
2753
+ * `/vortex <sub>` — Root command for VortEX instance operations.
2754
+ *
2755
+ * Subcommands:
2756
+ * init — first-time setup wizard (user profile + first worklog; detects
2757
+ * external folders in $HOME and surfaces an import hint)
2758
+ * status — instance state report (counts, profile, latestWorklog, missing)
2759
+ * import — copy a folder into the instance, auto-classify into 5 categories
2760
+ * or preserve user folder structure, inject frontmatter, scan
2761
+ * wiki-link health via @vortex-os/link-rewriter
2762
+ * doctor — 8-check health diagnosis (system dirs, profile, indexes,
2763
+ * wiki links, data-lint rules, runbook aging, node version,
2764
+ * git remote)
2765
+ * sync — framework-developer workflow: git pull → npm install → npm run
2766
+ * build → npm run verify. Stops on first failure with stdout/stderr
2767
+ * tail. End users who consume vortex from npm registry do not need
2768
+ * this — they get pre-built dist via `npm install @vortex-os/*`.
2769
+ * help — list available subcommands
2770
+ */
2771
+ declare const PLANNED_SUBS: readonly [];
2772
+ type PlannedSub = (typeof PLANNED_SUBS)[number];
2773
+ interface VortexInitResult {
2774
+ readonly subcommand: "init";
2775
+ readonly status: "completed" | "needs-input" | "already-initialized";
2776
+ readonly created: readonly string[];
2777
+ readonly nextActions: readonly string[];
2778
+ readonly missingInputs?: readonly {
2779
+ name: string;
2780
+ prompt: string;
2781
+ }[];
2782
+ readonly externalFolders?: readonly {
2783
+ readonly path: string;
2784
+ readonly basename: string;
2785
+ readonly mdCount: number;
2786
+ }[];
2787
+ }
2788
+ interface VortexPlannedResult {
2789
+ readonly subcommand: PlannedSub | "unknown";
2790
+ readonly status: "not-implemented";
2791
+ readonly message: string;
2792
+ }
2793
+ interface VortexHelpResult {
2794
+ readonly subcommand: "help";
2795
+ readonly status: "ok";
2796
+ /** `/vortex <sub>` subcommands routed inside this command. */
2797
+ readonly subcommands: readonly {
2798
+ readonly name: string;
2799
+ readonly description: string;
2800
+ readonly state: "active" | "planned";
2801
+ }[];
2802
+ /**
2803
+ * Other top-level slash commands shipped by the same plugin
2804
+ * (`session-rituals`). Independent commands, not `/vortex` subcommands —
2805
+ * surfaced here so users discover the full plugin surface from a single
2806
+ * `/vortex help` call instead of needing to know they exist separately.
2807
+ */
2808
+ readonly siblingCommands: readonly {
2809
+ readonly name: string;
2810
+ readonly description: string;
2811
+ }[];
2812
+ }
2813
+ interface VortexStatusResult {
2814
+ readonly subcommand: "status";
2815
+ readonly status: "ok" | "uninitialized";
2816
+ readonly instance: {
2817
+ readonly dataDir: string;
2818
+ readonly initialized: boolean;
2819
+ readonly profile?: {
2820
+ readonly name?: string;
2821
+ readonly role?: string;
2822
+ };
2823
+ };
2824
+ readonly counts: {
2825
+ readonly memory: number;
2826
+ readonly worklog: number;
2827
+ readonly decisionLog: number;
2828
+ readonly runbooks: number;
2829
+ readonly hubs: number;
2830
+ };
2831
+ readonly latestWorklog?: {
2832
+ readonly date: string;
2833
+ readonly keyword: string;
2834
+ readonly path: string;
2835
+ };
2836
+ readonly missing: readonly string[];
2837
+ readonly nextActions: readonly string[];
2838
+ }
2839
+ interface VortexImportResult {
2840
+ readonly subcommand: "import";
2841
+ readonly status: "completed" | "dry-run" | "needs-input" | "source-missing";
2842
+ readonly source?: string;
2843
+ readonly totalFiles: number;
2844
+ readonly copied: number;
2845
+ readonly classified: {
2846
+ readonly worklog: number;
2847
+ readonly decisionLog: number;
2848
+ readonly runbooks: number;
2849
+ readonly hubs: number;
2850
+ readonly memory: number;
2851
+ readonly preserved: number;
2852
+ };
2853
+ readonly frontmatterInjected: number;
2854
+ readonly frontmatterPreserved: number;
2855
+ readonly systemDirsCreated: readonly string[];
2856
+ readonly skipped: number;
2857
+ readonly links?: {
2858
+ readonly filesScanned: number;
2859
+ readonly total: number;
2860
+ readonly resolved: number;
2861
+ readonly broken: number;
2862
+ readonly ambiguous: number;
2863
+ };
2864
+ readonly missingInputs?: readonly {
2865
+ name: string;
2866
+ prompt: string;
2867
+ }[];
2868
+ readonly nextActions: readonly string[];
2869
+ }
2870
+ type DoctorCheckStatus = "pass" | "warn" | "fail" | "info";
2871
+ interface DoctorCheck {
2872
+ readonly id: string;
2873
+ readonly label: string;
2874
+ readonly status: DoctorCheckStatus;
2875
+ readonly detail?: string;
2876
+ }
2877
+ interface VortexDoctorResult {
2878
+ readonly subcommand: "doctor";
2879
+ readonly status: "ok" | "warnings" | "errors";
2880
+ readonly checks: readonly DoctorCheck[];
2881
+ readonly summary: {
2882
+ readonly pass: number;
2883
+ readonly warn: number;
2884
+ readonly fail: number;
2885
+ readonly info: number;
2886
+ };
2887
+ readonly nextActions: readonly string[];
2888
+ }
2889
+ type VortexSyncStepId = "pull" | "install" | "build" | "verify";
2890
+ type VortexSyncStepStatus = "ok" | "failed" | "skipped";
2891
+ interface VortexSyncStep {
2892
+ readonly id: VortexSyncStepId;
2893
+ readonly label: string;
2894
+ readonly status: VortexSyncStepStatus;
2895
+ readonly exitCode?: number;
2896
+ readonly durationMs?: number;
2897
+ readonly stdoutTail?: string;
2898
+ readonly stderrTail?: string;
2899
+ }
2900
+ interface VortexSyncResult {
2901
+ readonly subcommand: "sync";
2902
+ readonly status: "completed" | "failed" | "dry-run";
2903
+ readonly steps: readonly VortexSyncStep[];
2904
+ readonly failedAt?: VortexSyncStepId;
2905
+ readonly nextActions: readonly string[];
2906
+ }
2907
+ type VortexResult = VortexInitResult | VortexStatusResult | VortexImportResult | VortexDoctorResult | VortexSyncResult | VortexPlannedResult | VortexHelpResult;
2908
+ declare const vortexCommand: Command<VortexResult>;
2909
+
2910
+ //# sourceMappingURL=index.d.ts.map
2911
+
2912
+ type index_d_AgendaReport = AgendaReport;
2913
+ type index_d_AmbientRecallFactoryOptions = AmbientRecallFactoryOptions;
2914
+ type index_d_CatchUpOptions = CatchUpOptions;
2915
+ type index_d_CatchUpResult = CatchUpResult;
2916
+ type index_d_ClaudeSettings = ClaudeSettings;
2917
+ type index_d_CollectAgendaOptions = CollectAgendaOptions;
2918
+ type index_d_CurateAnyProposal = CurateAnyProposal;
2919
+ type index_d_CurateOptions = CurateOptions;
2920
+ type index_d_CurateResult = CurateResult;
2921
+ type index_d_EnsureHooksResult = EnsureHooksResult;
2922
+ type index_d_EnsureWorklogResult = EnsureWorklogResult;
2923
+ type index_d_GitPullResult = GitPullResult;
2924
+ type index_d_NewDecisionResult = NewDecisionResult;
2925
+ type index_d_OpenDecision = OpenDecision;
2926
+ type index_d_OpenTask = OpenTask;
2927
+ type index_d_RecallOptions = RecallOptions;
2928
+ type index_d_RecentWorklog = RecentWorklog;
2929
+ type index_d_ReindexResult = ReindexResult;
2930
+ type index_d_RitualRegistryOptions = RitualRegistryOptions;
2931
+ declare const index_d_SESSION_END_COMMAND: typeof SESSION_END_COMMAND;
2932
+ declare const index_d_SESSION_START_COMMAND: typeof SESSION_START_COMMAND;
2933
+ type index_d_SessionStartHookReport = SessionStartHookReport;
2934
+ type index_d_SessionStartReport = SessionStartReport;
2935
+ type index_d_VortexHelpResult = VortexHelpResult;
2936
+ type index_d_VortexInitResult = VortexInitResult;
2937
+ type index_d_VortexPlannedResult = VortexPlannedResult;
2938
+ type index_d_VortexResult = VortexResult;
2939
+ type index_d_VortexSyncResult = VortexSyncResult;
2940
+ type index_d_VortexSyncStep = VortexSyncStep;
2941
+ type index_d_VortexSyncStepId = VortexSyncStepId;
2942
+ type index_d_VortexSyncStepStatus = VortexSyncStepStatus;
2943
+ type index_d_WorklogAppendResult = WorklogAppendResult;
2944
+ declare const index_d_agendaCommand: typeof agendaCommand;
2945
+ declare const index_d_catchUpSessions: typeof catchUpSessions;
2946
+ declare const index_d_collectAgenda: typeof collectAgenda;
2947
+ declare const index_d_collectSessionStartReport: typeof collectSessionStartReport;
2948
+ declare const index_d_createAmbientRecaller: typeof createAmbientRecaller;
2949
+ declare const index_d_createRitualRegistry: typeof createRitualRegistry;
2950
+ declare const index_d_curateCommand: typeof curateCommand;
2951
+ declare const index_d_decisionCommand: typeof decisionCommand;
2952
+ declare const index_d_detectWorklogGaps: typeof detectWorklogGaps;
2953
+ declare const index_d_ensureVortexHooks: typeof ensureVortexHooks;
2954
+ declare const index_d_ensureWorklogEntry: typeof ensureWorklogEntry;
2955
+ declare const index_d_extractNextUp: typeof extractNextUp;
2956
+ declare const index_d_extractOpenTasks: typeof extractOpenTasks;
2957
+ declare const index_d_logCommand: typeof logCommand;
2958
+ declare const index_d_parseSettings: typeof parseSettings;
2959
+ declare const index_d_recallCommand: typeof recallCommand;
2960
+ declare const index_d_reindexCommand: typeof reindexCommand;
2961
+ declare const index_d_renderAgenda: typeof renderAgenda;
2962
+ declare const index_d_renderSessionStartReport: typeof renderSessionStartReport;
2963
+ declare const index_d_serializeSettings: typeof serializeSettings;
2964
+ declare const index_d_sessionStartCommand: typeof sessionStartCommand;
2965
+ declare const index_d_vortexCommand: typeof vortexCommand;
2966
+ declare namespace index_d {
2967
+ export { type index_d_AgendaReport as AgendaReport, type index_d_AmbientRecallFactoryOptions as AmbientRecallFactoryOptions, type index_d_CatchUpOptions as CatchUpOptions, type index_d_CatchUpResult as CatchUpResult, type index_d_ClaudeSettings as ClaudeSettings, type index_d_CollectAgendaOptions as CollectAgendaOptions, type index_d_CurateAnyProposal as CurateAnyProposal, type index_d_CurateOptions as CurateOptions, type index_d_CurateResult as CurateResult, type index_d_EnsureHooksResult as EnsureHooksResult, type index_d_EnsureWorklogResult as EnsureWorklogResult, type index_d_GitPullResult as GitPullResult, type index_d_NewDecisionResult as NewDecisionResult, type index_d_OpenDecision as OpenDecision, type index_d_OpenTask as OpenTask, type index_d_RecallOptions as RecallOptions, type index_d_RecentWorklog as RecentWorklog, type index_d_ReindexResult as ReindexResult, type index_d_RitualRegistryOptions as RitualRegistryOptions, index_d_SESSION_END_COMMAND as SESSION_END_COMMAND, index_d_SESSION_START_COMMAND as SESSION_START_COMMAND, type index_d_SessionStartHookReport as SessionStartHookReport, type index_d_SessionStartReport as SessionStartReport, type index_d_VortexHelpResult as VortexHelpResult, type index_d_VortexInitResult as VortexInitResult, type index_d_VortexPlannedResult as VortexPlannedResult, type index_d_VortexResult as VortexResult, type index_d_VortexSyncResult as VortexSyncResult, type index_d_VortexSyncStep as VortexSyncStep, type index_d_VortexSyncStepId as VortexSyncStepId, type index_d_VortexSyncStepStatus as VortexSyncStepStatus, type index_d_WorklogAppendResult as WorklogAppendResult, index_d_agendaCommand as agendaCommand, index_d_catchUpSessions as catchUpSessions, index_d_collectAgenda as collectAgenda, index_d_collectSessionStartReport as collectSessionStartReport, index_d_createAmbientRecaller as createAmbientRecaller, index_d_createRitualRegistry as createRitualRegistry, index_d_curateCommand as curateCommand, index_d_decisionCommand as decisionCommand, index_d_detectWorklogGaps as detectWorklogGaps, index_d_ensureVortexHooks as ensureVortexHooks, index_d_ensureWorklogEntry as ensureWorklogEntry, index_d_extractNextUp as extractNextUp, index_d_extractOpenTasks as extractOpenTasks, index_d_logCommand as logCommand, index_d_parseSettings as parseSettings, index_d_recallCommand as recallCommand, index_d_reindexCommand as reindexCommand, index_d_renderAgenda as renderAgenda, index_d_renderSessionStartReport as renderSessionStartReport, index_d_serializeSettings as serializeSettings, index_d_sessionStartCommand as sessionStartCommand, index_d_vortexCommand as vortexCommand };
2968
+ }
2969
+
2970
+ export { index_d$9 as aiCodingPitfalls, index_d$d as core, index_d$a as dataLint, index_d$5 as decisionLog, index_d$4 as indexGenerator, index_d$2 as linkRewriter, index_d$b as memorySystem, index_d$1 as proactiveCurator, index_d$7 as reportGenerator, index_d$3 as runbooks, index_d as sessionRituals, index_d$c as slashCommands, index_d$8 as toolRules, index_d$6 as worklog };