diffwiki-core 0.4.0-rc.202607221918.a124bf3 → 0.4.0

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,5 +1,3 @@
1
- import { LogLevel, Logger } from '@logtape/logtape';
2
-
3
1
  type CollectionType = 'global' | 'repo';
4
2
  interface CollectionEntry {
5
3
  name: string;
@@ -14,7 +12,6 @@ interface Registry {
14
12
  }
15
13
  interface Config {
16
14
  defaultCollection?: string;
17
- defaultSearch?: string;
18
15
  }
19
16
  interface Article {
20
17
  title: string;
@@ -53,90 +50,6 @@ interface ArticleContent {
53
50
  /** Absolute path to the file on disk. */
54
51
  path: string;
55
52
  }
56
- type PluginKind = 'search';
57
- declare const NATIVE_ENGINE = "native";
58
- interface QueryOptions {
59
- engine?: string;
60
- collection?: string;
61
- type?: string;
62
- limit?: number;
63
- }
64
- interface QueryHit {
65
- collection: string;
66
- path: string;
67
- title: string;
68
- tags: string[];
69
- snippet?: string;
70
- score?: number;
71
- docid?: string;
72
- }
73
- type DiagnosticLevel = 'ok' | 'warn' | 'error';
74
- interface Diagnostic {
75
- level: DiagnosticLevel;
76
- message: string;
77
- }
78
- interface PluginRecord {
79
- name: string;
80
- kind: PluginKind;
81
- command: string[];
82
- version?: string;
83
- /** Capabilities cached at install/register time via the `describe` op. */
84
- capabilities?: PluginCapabilities;
85
- enabled: boolean;
86
- managed?: boolean;
87
- }
88
- interface PluginRegistryFile {
89
- version: number;
90
- plugins: PluginRecord[];
91
- }
92
- /** Capabilities a plugin advertises via the `describe` op, cached in the registry. */
93
- interface PluginCapabilities {
94
- /** Search types the plugin supports (e.g. `['keyword', 'semantic', 'hybrid']`). */
95
- searchTypes: string[];
96
- /** Whether the plugin supports filtering results by collection. */
97
- collectionFilter: boolean;
98
- /** Whether the plugin handles lifecycle events (article/collection mutations). */
99
- events: boolean;
100
- }
101
- interface PluginReadiness {
102
- toolInstalled: boolean;
103
- modelsPresent: boolean;
104
- indexBuilt: boolean;
105
- docsIndexed: number;
106
- embeddingsPending: number;
107
- firstSearchCost: 'instant' | 'model-load' | 'model-download';
108
- }
109
- interface PluginHealth {
110
- ready: boolean;
111
- readiness?: PluginReadiness;
112
- diagnostics: Diagnostic[];
113
- }
114
- interface SearchMode {
115
- plugin: string;
116
- type: string;
117
- label: string;
118
- }
119
- type PluginEvent = 'collection-added' | 'collection-removed' | 'article-created' | 'article-updated' | 'article-removed';
120
- /**
121
- * Host-side facade for a search plugin. Each method is backed by a one-shot
122
- * subprocess invocation -- there is no persistent process and no `close()`.
123
- */
124
- interface SearchProvider {
125
- /** Plugin name. */
126
- name: string;
127
- /** Plugin capabilities (cached from the `describe` op). */
128
- capabilities: PluginCapabilities;
129
- /** Run a search query and return hits with absolute paths. */
130
- handleSearch(term: string, collections: CollectionEntry[], opts?: QueryOptions): Promise<QueryHit[]>;
131
- /** Rebuild the search index. Returns the number of documents indexed. */
132
- reindex(collections: CollectionEntry[], opts?: {
133
- embed?: boolean;
134
- }): Promise<number>;
135
- /** Emit a lifecycle event to the plugin (best-effort). */
136
- emitEvent(event: PluginEvent, collection: string, relPath: string | undefined, collections: CollectionEntry[]): Promise<void>;
137
- /** Check plugin health and readiness. */
138
- health(collections: CollectionEntry[]): Promise<PluginHealth>;
139
- }
140
53
 
141
54
  /** Base class for all expected, user-facing diffwiki failures. */
142
55
  declare class DiffwikiError extends Error {
@@ -157,8 +70,6 @@ declare class ArticleNotFoundError extends DiffwikiError {
157
70
  declare class InvalidTargetError extends DiffwikiError {
158
71
  constructor(target: string, expected: string);
159
72
  }
160
- declare class PluginError extends DiffwikiError {
161
- }
162
73
 
163
74
  /** Root of all global diffwiki state. Overridable via DIFFWIKI_HOME (used by tests). */
164
75
  declare function resolveHome(): string;
@@ -166,8 +77,6 @@ declare function registryPath(): string;
166
77
  declare function configPath(): string;
167
78
  declare function collectionsDir(): string;
168
79
  declare function collectionDir(name: string): string;
169
- declare function pluginsDir(): string;
170
- declare function pluginsRegistryPath(): string;
171
80
 
172
81
  declare function readConfig(): Promise<Config>;
173
82
  declare function writeConfig(config: Config): Promise<void>;
@@ -239,147 +148,25 @@ interface InitOptions {
239
148
  }
240
149
  declare function initRepoWiki(opts: InitOptions): Promise<CollectionEntry>;
241
150
 
242
- /**
243
- * Search query routing.
244
- *
245
- * `query()` is the primary search entry point. It resolves the search engine
246
- * (explicit opt, config default, first enabled plugin, or native BM25), runs
247
- * the search, and falls back to native on plugin failure. The built-in native
248
- * search is a zero-dependency BM25-ranked full-text search across title, body,
249
- * tags, and collection name.
250
- *
251
- * @module
252
- */
253
-
254
- /**
255
- * Built-in JS-native search: BM25-ranked full-text search over an article's
256
- * title, body, tags, and collection name -- across every registered collection's
257
- * ABSOLUTE registry path (so in-repo wikis outside ~/.diffwiki are searched too).
258
- * No `find`/OS shelling -- cross-platform and dependency-free.
259
- *
260
- * When `term` is empty, returns all articles unranked (no score) to preserve
261
- * the "browse all" behavior.
262
- */
263
- declare function nativeSearch(term: string, collections: CollectionEntry[], collection?: string): Promise<QueryHit[]>;
264
- /**
265
- * Resolve the engine + type that `query()` would use when the caller passes no
266
- * explicit engine/type. Reads cached capabilities from the registry -- no spawn.
267
- */
268
- declare function resolveDefaultSearchMode(): Promise<{
269
- engine: string;
270
- type: string;
271
- }>;
272
- /**
273
- * The result of a {@link search} call: the hits plus the metadata a host UI
274
- * needs to tell the user WHICH engine ran and whether it silently degraded.
275
- *
276
- * `fellBackToNative` is true whenever the resolved engine could not serve the
277
- * search (no provider, or `handleSearch` threw) and native BM25 produced these
278
- * hits instead. `error`, when set, is the captured message from the failing
279
- * engine — a failing engine is thus distinguishable from an engine that
280
- * legitimately found zero results.
281
- */
282
- interface SearchOutcome {
283
- /** The hits to display (from the engine, or from native on fallback). */
284
- hits: QueryHit[];
285
- /** The engine that was resolved and attempted (may differ from what produced `hits`). */
286
- engine: string;
287
- /** True when native BM25 produced `hits` because the engine failed/was unavailable. */
288
- fellBackToNative: boolean;
289
- /** The captured failure message from the engine, when it errored. */
290
- error?: string;
151
+ interface QueryHit {
152
+ collection: string;
153
+ path: string;
154
+ title: string;
155
+ tags: string[];
291
156
  }
292
157
  /**
293
- * Search articles via the resolved search engine, surfacing engine failures.
294
- *
295
- * Routing precedence: explicit `opts.engine` -> `config.defaultSearch` ->
296
- * first enabled plugin -> native BM25.
297
- *
298
- * On ANY engine failure (no provider, or `handleSearch` throws) the error
299
- * message is CAPTURED, native BM25 is run as a fallback, and the outcome
300
- * reports `{ fellBackToNative: true, error }`. On success it reports
301
- * `{ fellBackToNative: false }` with the engine's hits. This lets callers
302
- * distinguish "the engine broke" from "the engine found nothing".
303
- */
304
- declare function search(term: string, opts?: QueryOptions): Promise<SearchOutcome>;
305
- /**
306
- * Search articles via the resolved search engine (native fallback on plugin
307
- * failure). A thin wrapper over {@link search} that returns just the hits;
308
- * engine errors are swallowed. Use {@link search} to surface those errors.
309
- *
310
- * Routing precedence: explicit `opts.engine` -> `config.defaultSearch` ->
311
- * first enabled plugin -> native BM25.
158
+ * DUMMY search: case-insensitive substring match over title, filename, and tags.
159
+ * Real QMD-based semantic search replaces this later.
312
160
  */
313
- declare function query(term: string, opts?: QueryOptions): Promise<QueryHit[]>;
161
+ declare function query(term: string, collection?: string): Promise<QueryHit[]>;
314
162
 
315
- /**
316
- * Diagnostics and health checks.
317
- *
318
- * `doctor()` inspects the diffwiki home directory, registry, collections,
319
- * config, and every registered plugin to produce a list of human-readable
320
- * diagnostics at ok/warn/error severity levels.
321
- *
322
- * @module
323
- */
324
-
325
- /** Run all diagnostics and return the results. */
163
+ type DiagnosticLevel = 'ok' | 'warn' | 'error';
164
+ interface Diagnostic {
165
+ level: DiagnosticLevel;
166
+ message: string;
167
+ }
326
168
  declare function doctor(): Promise<Diagnostic[]>;
327
169
 
328
- /**
329
- * Centralized logging for diffwiki host code, built on LogTape.
330
- *
331
- * diffwiki-core (and the plugin SDK) are LIBRARIES: they obtain loggers via
332
- * {@link getLogger} and emit records, but they NEVER call LogTape's
333
- * `configure()`/`configureSync()`. Per the LogTape library-vs-app pattern, a
334
- * library stays silent until the surrounding APPLICATION (the CLI or the web
335
- * app) installs sinks. `getLogger` called before any `configure` is a safe
336
- * no-op, so importing this module has no side effects.
337
- *
338
- * All diffwiki loggers live under the `['diffwiki', ...]` category so an app can
339
- * route the whole tree with a single logger config entry. Plugin diagnostics
340
- * are forwarded under `['diffwiki', 'plugin', <name>]` (see `plugins/host.ts`).
341
- *
342
- * [DEVIATION] The task brief lists the level name `warn`, but the installed
343
- * `@logtape/logtape` (2.2.4) names that level `warning` (levels are
344
- * `trace|debug|info|warning|error|fatal`). We follow the installed types:
345
- * {@link DiffwikiLogLevel} uses `warning`, and {@link resolveLogLevel} defaults
346
- * to `warning`. Note LogTape's Logger METHOD for that level is still `.warn()`.
347
- *
348
- * @module
349
- */
350
-
351
- /**
352
- * A diffwiki log level. Identical to LogTape's `LogLevel`
353
- * (`trace|debug|info|warning|error|fatal`); re-exported so host and app code can
354
- * refer to it without importing LogTape directly.
355
- */
356
- type DiffwikiLogLevel = LogLevel;
357
- /**
358
- * Get a diffwiki logger under the shared `['diffwiki', ...]` category root.
359
- *
360
- * This is a thin wrapper over LogTape's `getLogger`. It does NOT configure
361
- * LogTape — libraries stay silent until an app calls `configure()`. Passing a
362
- * string scope is sugar for a single-element category array.
363
- *
364
- * @param scope - A scope name (e.g. `'query'`) or a nested category path
365
- * (e.g. `['plugin', 'diffwiki-qmd']`).
366
- * @returns A LogTape {@link Logger} under `['diffwiki', ...scope]`.
367
- */
368
- declare function getLogger(scope: string | string[]): Logger;
369
- /**
370
- * Resolve the diffwiki log level from the environment.
371
- *
372
- * Reads `DIFFWIKI_LOG` (one of `trace|debug|info|warning|error|fatal`, case
373
- * insensitive) and returns it, falling back to the documented default
374
- * (`warning`) when unset or unrecognized. Apps use this to pick the app-level
375
- * sink's `lowestLevel` at startup.
376
- *
377
- * @param env - The environment to read (defaults to `process.env`). Injectable
378
- * for tests.
379
- * @returns The resolved {@link DiffwikiLogLevel}.
380
- */
381
- declare function resolveLogLevel(env?: NodeJS.ProcessEnv): DiffwikiLogLevel;
382
-
383
170
  /**
384
171
  * Build a nested ArticleNode[] from a flat list of relative file paths.
385
172
  * Pure — no I/O. Directories become branches; files become leaves whose `slug`
@@ -421,166 +208,4 @@ interface RenderedArticle {
421
208
  */
422
209
  declare function renderArticle(body: string): Promise<RenderedArticle>;
423
210
 
424
- /**
425
- * Host-side plugin invocation and search-provider loading.
426
- *
427
- * `invokePlugin()` spawns a plugin subprocess, writes a single JSON request to
428
- * stdin (then closes it), reads the JSON response from stdout, and lets the
429
- * process exit. Progress/diagnostic logs arrive on STDERR as structured
430
- * `{log:{level,message}}` JSON lines; the host forwards each to a LogTape logger
431
- * scoped by plugin name and keeps the raw stderr for on-error context.
432
- *
433
- * `loadSearchProvider()` returns a `SearchProvider` backed by one-shot
434
- * invocations — each method call spawns the plugin fresh. `listSearchModes()`
435
- * reads cached capabilities from the registry rather than spawning each plugin.
436
- *
437
- * @module
438
- */
439
-
440
- /**
441
- * Load a search provider backed by one-shot subprocess invocations. Each method
442
- * on the returned `SearchProvider` spawns the plugin binary, sends one JSON
443
- * request, and reads the response. Returns `null` if no matching enabled plugin
444
- * is found or the plugin fails its `describe` handshake.
445
- *
446
- * @param name - Optional plugin name. If omitted, the first enabled search plugin is used.
447
- */
448
- declare function loadSearchProvider(name?: string): Promise<SearchProvider | null>;
449
- /**
450
- * List all available search modes, starting with native BM25. Reads cached
451
- * capabilities from the plugin registry rather than spawning each plugin.
452
- * Plugins without cached capabilities (registered before the SDK refactor) are
453
- * skipped -- re-register to populate them.
454
- */
455
- declare function listSearchModes(): Promise<SearchMode[]>;
456
-
457
- /**
458
- * Plugin lifecycle management.
459
- *
460
- * Handles installing (from npm), registering (from an existing binary),
461
- * removing, and listing plugins. Installation and registration both run
462
- * a `describe` op to validate the plugin and cache its capabilities in
463
- * the registry, then a best-effort `setup` op to provision the plugin.
464
- *
465
- * @module
466
- */
467
-
468
- /** Create the managed plugin root (an isolated npm project) and return its path. */
469
- declare function ensurePluginRoot(): Promise<string>;
470
- /** One provisioning step reported by a plugin's `setup` op. */
471
- interface SetupStep {
472
- step: string;
473
- ok: boolean;
474
- message?: string;
475
- }
476
- /** The captured result of a plugin's `setup` op. */
477
- interface SetupOutcome {
478
- /** Whether the plugin provisioned to a ready-to-search state. */
479
- ready: boolean;
480
- /** Per-step results (e.g. verify-qmd, add-collections, index). */
481
- steps: SetupStep[];
482
- }
483
- /** The result of {@link installPlugin}: the registered record + the setup outcome (if setup ran). */
484
- interface InstallOutcome {
485
- record: PluginRecord;
486
- /** The setup result, or undefined when setup was skipped (`opts.setup === false`). */
487
- setup?: SetupOutcome;
488
- }
489
- /**
490
- * Install an npm spec into the managed root, register the discovered search bin,
491
- * run a `describe` to validate and cache capabilities, then (unless `opts.setup
492
- * === false`) run the plugin's `setup` op so the user lands on a working search.
493
- */
494
- declare function installPlugin(spec: string, opts?: {
495
- setup?: boolean;
496
- prefetchModels?: boolean;
497
- }): Promise<InstallOutcome>;
498
- /**
499
- * Register an existing executable as a search plugin. Validates by running
500
- * `describe`, caches capabilities, then runs `setup` best-effort.
501
- */
502
- declare function registerPlugin(name: string, command: string[]): Promise<PluginRecord>;
503
- /** Uninstall a managed plugin's package and deregister it. */
504
- declare function removePlugin(name: string): Promise<void>;
505
- /** List every registered plugin, marking whether it currently responds to `describe`. */
506
- declare function listPlugins(): Promise<Array<PluginRecord & {
507
- spawnable: boolean;
508
- }>>;
509
-
510
- /**
511
- * Plugin lifecycle event emission.
512
- *
513
- * Fires a lifecycle event at the active search plugin -- best-effort and
514
- * non-blocking. diffwiki emits events on every content mutation (article
515
- * create/update/remove, collection add/remove). A plugin's `onEvent`
516
- * handler can self-manage in response (e.g. incremental reindex).
517
- *
518
- * Events are an optimisation, not the correctness path: the mtime-staleness
519
- * lazy reindex on `search` remains the guarantee, so a dropped event only
520
- * costs a later lazy rebuild, never wrong results.
521
- *
522
- * @module
523
- */
524
-
525
- /**
526
- * Emit a lifecycle event to the active search plugin.
527
- *
528
- * This function is designed to be called fire-and-forget: `void emitPluginEvent(...)`.
529
- * It only invokes the plugin when the plugin has `capabilities.events === true`.
530
- * Every error is swallowed -- a slow or absent plugin can neither stall nor
531
- * fail a content mutation.
532
- */
533
- declare function emitPluginEvent(event: PluginEvent, collection: string, relPath?: string): Promise<void>;
534
-
535
- /**
536
- * Known plugin catalog.
537
- *
538
- * A built-in directory of official diffwiki plugins that users can install.
539
- * This is discovery only -- installing goes through `installPlugin(spec)`.
540
- *
541
- * @module
542
- */
543
-
544
- /**
545
- * A plugin diffwiki knows about and can recommend — the "app store" catalog.
546
- * Discovery only; installing still goes through `installPlugin(spec)`.
547
- */
548
- interface KnownPlugin {
549
- name: string;
550
- kind: PluginKind;
551
- /** The argument for `diffwiki plugin install <spec>` (the npm package name). */
552
- spec: string;
553
- description: string;
554
- /** External tool the plugin drives, shown as a prerequisite hint. */
555
- requires?: string;
556
- }
557
- /** Return a copy of the known/official plugins available to install. */
558
- declare function listAvailablePlugins(): KnownPlugin[];
559
-
560
- /**
561
- * Plugin registry persistence.
562
- *
563
- * Manages the `~/.diffwiki/plugins.json` file that stores plugin records.
564
- * Each record includes the plugin's name, kind, command argv, version,
565
- * cached capabilities (populated at install/register time via `describe`),
566
- * and enabled/managed flags.
567
- *
568
- * @module
569
- */
570
-
571
- /** Read the plugin registry from disk. Returns an empty registry if the file does not exist. */
572
- declare function readPluginRegistry(): Promise<PluginRegistryFile>;
573
- /** Write the plugin registry to disk, creating the home directory if needed. */
574
- declare function writePluginRegistry(registry: PluginRegistryFile): Promise<void>;
575
- /** Insert or replace the plugin record with the same name. */
576
- declare function upsertPlugin(record: PluginRecord): Promise<void>;
577
- /** Remove a plugin record by name. */
578
- declare function deregisterPlugin(name: string): Promise<void>;
579
- /** List all enabled plugins of a given kind. */
580
- declare function listEnabledPlugins(kind: PluginKind): Promise<PluginRecord[]>;
581
- /** Find a single enabled plugin by kind and optionally by name. Returns the first if name is omitted. */
582
- declare function findEnabledPlugin(kind: PluginKind, name?: string): Promise<PluginRecord | undefined>;
583
- /** Find a plugin record by name (regardless of enabled state). */
584
- declare function findPluginRecord(name: string): Promise<PluginRecord | undefined>;
585
-
586
- export { type Article, type ArticleContent, type ArticleNode, ArticleNotFoundError, type CollectionEntry, CollectionExistsError, CollectionNotFoundError, type CollectionType, type Config, type Diagnostic, type DiagnosticLevel, DiffwikiError, type DiffwikiLogLevel, type InitOptions, type InstallOutcome, InvalidTargetError, type KnownPlugin, NATIVE_ENGINE, NoDefaultCollectionError, type ParsedArticle, type PluginCapabilities, PluginError, type PluginEvent, type PluginHealth, type PluginKind, type PluginReadiness, type PluginRecord, type PluginRegistryFile, type QueryHit, type QueryOptions, REPO_CONFIG_FILE, type Registry, type RenderedArticle, type RepoConfig, type SearchMode, type SearchOutcome, type SearchProvider, type SerializableArticle, type SetupOutcome, type SetupStep, type TocItem, addTags, buildArticleTree, collectionDir, collectionsDir, configPath, createArticle, createCollection, deregisterPlugin, doctor, emitPluginEvent, ensurePluginRoot, findCollection, findEnabledPlugin, findPluginRecord, getConfigValue, getLogger, initRepoWiki, installPlugin, listArticleTree, listAvailablePlugins, listCollections, listEnabledPlugins, listPlugins, listSearchModes, loadSearchProvider, nativeSearch, parseAddTarget, parseArticle, parsePathTarget, pluginsDir, pluginsRegistryPath, query, readArticle, readConfig, readPluginRegistry, readRegistry, readRepoConfig, registerCollection, registerPlugin, registryPath, removeArticle, removeCollection, removePlugin, removeTags, renderArticle, resolveDefaultSearchMode, resolveHome, resolveLogLevel, search, serializeArticle, setConfigValue, setTags, slugify, unregisterCollection, updateArticleBody, upsertPlugin, writeConfig, writePluginRegistry, writeRegistry };
211
+ export { type Article, type ArticleContent, type ArticleNode, ArticleNotFoundError, type CollectionEntry, CollectionExistsError, CollectionNotFoundError, type CollectionType, type Config, type Diagnostic, type DiagnosticLevel, DiffwikiError, type InitOptions, InvalidTargetError, NoDefaultCollectionError, type ParsedArticle, type QueryHit, REPO_CONFIG_FILE, type Registry, type RenderedArticle, type RepoConfig, type SerializableArticle, type TocItem, addTags, buildArticleTree, collectionDir, collectionsDir, configPath, createArticle, createCollection, doctor, findCollection, getConfigValue, initRepoWiki, listArticleTree, listCollections, parseAddTarget, parseArticle, parsePathTarget, query, readArticle, readConfig, readRegistry, readRepoConfig, registerCollection, registryPath, removeArticle, removeCollection, removeTags, renderArticle, resolveHome, serializeArticle, setConfigValue, setTags, slugify, unregisterCollection, updateArticleBody, writeConfig, writeRegistry };