@ncukondo/search-hub 0.16.0 → 0.18.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.
Files changed (37) hide show
  1. package/dist/cli/commands/query/assess.d.ts +15 -0
  2. package/dist/cli/commands/query/assess.d.ts.map +1 -0
  3. package/dist/cli/commands/query/assess.js +38 -0
  4. package/dist/cli/commands/query/assess.js.map +1 -0
  5. package/dist/cli/commands/query/iteration-log.d.ts +58 -0
  6. package/dist/cli/commands/query/iteration-log.d.ts.map +1 -0
  7. package/dist/cli/commands/query/iteration-log.js +115 -0
  8. package/dist/cli/commands/query/iteration-log.js.map +1 -0
  9. package/dist/cli/commands/query/log.d.ts +9 -0
  10. package/dist/cli/commands/query/log.d.ts.map +1 -0
  11. package/dist/cli/commands/query/log.js +64 -0
  12. package/dist/cli/commands/query/log.js.map +1 -0
  13. package/dist/cli/commands/review/extract.d.ts.map +1 -1
  14. package/dist/cli/commands/review/extract.js +10 -2
  15. package/dist/cli/commands/review/extract.js.map +1 -1
  16. package/dist/cli/commands/review/finalize.d.ts +2 -0
  17. package/dist/cli/commands/review/finalize.d.ts.map +1 -1
  18. package/dist/cli/commands/review/finalize.js +12 -1
  19. package/dist/cli/commands/review/finalize.js.map +1 -1
  20. package/dist/cli/commands/review/init.d.ts.map +1 -1
  21. package/dist/cli/commands/review/init.js +5 -21
  22. package/dist/cli/commands/review/init.js.map +1 -1
  23. package/dist/cli/commands/review/schema.d.ts +199 -0
  24. package/dist/cli/commands/review/schema.d.ts.map +1 -0
  25. package/dist/cli/commands/review/schema.js +77 -0
  26. package/dist/cli/commands/review/schema.js.map +1 -0
  27. package/dist/cli/commands/review/types.d.ts +15 -70
  28. package/dist/cli/commands/review/types.d.ts.map +1 -1
  29. package/dist/cli/commands/review/types.js +2 -0
  30. package/dist/cli/commands/review/types.js.map +1 -1
  31. package/dist/cli/index.d.ts.map +1 -1
  32. package/dist/cli/index.js +89 -4
  33. package/dist/cli/index.js.map +1 -1
  34. package/dist/cli/suggestions/rules.d.ts.map +1 -1
  35. package/dist/cli/suggestions/rules.js +22 -2
  36. package/dist/cli/suggestions/rules.js.map +1 -1
  37. package/package.json +1 -1
package/dist/cli/index.js CHANGED
@@ -38,15 +38,20 @@ import { executeReviewMerge, formatMergeOutput as formatMergeOutput$1 } from "./
38
38
  import { executeReviewMark } from "./commands/review/mark.js";
39
39
  import { executeReviewExport, formatExportOutput } from "./commands/review/export.js";
40
40
  import { executeReviewFinalize, formatFinalizeOutput } from "./commands/review/finalize.js";
41
+ import "zod";
42
+ import "./commands/review/schema.js";
41
43
  import { registerFulltextCommands } from "./commands/fulltext/index.js";
42
44
  import { parseRegisterOptions, validateRegisterInput, hasReviewFile, getReviewSummary, formatNoIncludedArticlesError, formatPendingWarning, confirmPrompt, getIncludedArticles, formatReviewRequiredMessage, formatIgnoringReviewsNote, formatDryRunOutput as formatDryRunOutput$1, formatRegistrationSummary } from "./commands/register.js";
43
45
  import { formatSuggestion } from "./suggestions/index.js";
44
46
  import { getSuggestion } from "./suggestions/rules.js";
47
+ import { readLogEntries, computeQueryHash, appendLogEntry, buildPreviewLogEntry, buildCountLogEntry } from "./commands/query/iteration-log.js";
48
+ import { executeQueryAssess } from "./commands/query/assess.js";
49
+ import { formatLogOutput } from "./commands/query/log.js";
45
50
  import { registerArticles, saveRegistrationRecord } from "../integration/register.js";
46
51
  import { checkRefAvailable, checkNpmAvailable, installRefManager } from "../integration/ref-cli.js";
47
52
  import { loadSession, listSessions, sessionExists } from "../session/manager.js";
48
53
  import { parseQueryFile, detectShortKeywords } from "../query/parser.js";
49
- import { writeFile, readFile } from "node:fs/promises";
54
+ import { readFile, writeFile } from "node:fs/promises";
50
55
  import { realpathSync } from "node:fs";
51
56
  import { join } from "node:path";
52
57
  import { fileURLToPath } from "node:url";
@@ -346,6 +351,54 @@ Examples:
346
351
  process.exitCode = EXIT_CODES.GENERAL_ERROR;
347
352
  }
348
353
  });
354
+ queryCommand.command("assess").description("Record an assessment of the current query iteration").argument("<file>", "path to query YAML file").option("--verdict <verdict>", "assessment verdict (e.g., reject, good, refine)").option("--precision <precision>", "estimated precision (e.g., ~60%)").option("--comment <comment>", "free-text comment").addHelpText("after", `
355
+ Examples:
356
+ $ search-hub query assess query.yaml --verdict reject --comment "Too broad"
357
+ $ search-hub query assess query.yaml --verdict good --precision "~60%"`).action(async (file, options) => {
358
+ const globalOpts = program.opts();
359
+ try {
360
+ const result = await executeQueryAssess(file, options);
361
+ if (result.success) {
362
+ if (!globalOpts.quiet) {
363
+ console.log("Assessment recorded.");
364
+ const suggestion = formatSuggestion(getSuggestion({
365
+ command: "query assess",
366
+ queryFile: file
367
+ }));
368
+ if (suggestion) console.log(suggestion);
369
+ }
370
+ process.exitCode = EXIT_CODES.SUCCESS;
371
+ } else {
372
+ if (!globalOpts.quiet) {
373
+ console.error(`Error: ${result.error}`);
374
+ }
375
+ process.exitCode = EXIT_CODES.GENERAL_ERROR;
376
+ }
377
+ } catch (error) {
378
+ if (!globalOpts.quiet) {
379
+ console.error("Error:", error instanceof Error ? error.message : error);
380
+ }
381
+ process.exitCode = EXIT_CODES.GENERAL_ERROR;
382
+ }
383
+ });
384
+ queryCommand.command("log").description("View the query iteration history").argument("<file>", "path to query YAML file").option("--json", "output as JSON").addHelpText("after", `
385
+ Examples:
386
+ $ search-hub query log query.yaml
387
+ $ search-hub query log query.yaml --json`).action(async (file, options) => {
388
+ const globalOpts = program.opts();
389
+ try {
390
+ const entries = await readLogEntries(file);
391
+ if (!globalOpts.quiet) {
392
+ console.log(formatLogOutput(entries, { json: options?.json }));
393
+ }
394
+ process.exitCode = EXIT_CODES.SUCCESS;
395
+ } catch (error) {
396
+ if (!globalOpts.quiet) {
397
+ console.error("Error:", error instanceof Error ? error.message : error);
398
+ }
399
+ process.exitCode = EXIT_CODES.GENERAL_ERROR;
400
+ }
401
+ });
349
402
  program.command("status").description("Show session status and statistics").argument("[session-id]", "session ID to show details for").option("--json", "output as JSON").option("--all", "include completed sessions").addHelpText("after", `
350
403
  Examples:
351
404
  $ search-hub status # List recent sessions
@@ -501,8 +554,21 @@ Query features (use "query init" to see full template):
501
554
  process.exitCode = EXIT_CODES.GENERAL_ERROR;
502
555
  return;
503
556
  }
557
+ if (searchOpts.queryFile) {
558
+ try {
559
+ const qContent = await readFile(searchOpts.queryFile, "utf-8");
560
+ const qHash = computeQueryHash(qContent);
561
+ await appendLogEntry(searchOpts.queryFile, buildPreviewLogEntry(qHash, previews));
562
+ } catch {
563
+ }
564
+ }
504
565
  if (!globalOpts.quiet) {
505
566
  console.log(formatPreviewOutput(previews, searchOpts.queryFile));
567
+ const suggestion = formatSuggestion(getSuggestion({
568
+ command: "search --preview",
569
+ queryFile: searchOpts.queryFile
570
+ }));
571
+ if (suggestion) console.log(suggestion);
506
572
  }
507
573
  const previewHasErrors = previews.some((p) => p.error);
508
574
  const previewAllFailed = previews.every((p) => p.error);
@@ -536,6 +602,14 @@ Warning: Some providers failed:
536
602
  process.exitCode = EXIT_CODES.GENERAL_ERROR;
537
603
  return;
538
604
  }
605
+ if (searchOpts.queryFile) {
606
+ try {
607
+ const qContent = await readFile(searchOpts.queryFile, "utf-8");
608
+ const qHash = computeQueryHash(qContent);
609
+ await appendLogEntry(searchOpts.queryFile, buildCountLogEntry(qHash, counts));
610
+ } catch {
611
+ }
612
+ }
539
613
  if (!globalOpts.quiet) {
540
614
  console.log(formatCountOnlyOutput(counts, searchOpts.queryFile));
541
615
  const suggestion = formatSuggestion(getSuggestion({
@@ -1790,13 +1864,21 @@ Examples:
1790
1864
  process.exitCode = EXIT_CODES.SESSION_ERROR;
1791
1865
  }
1792
1866
  });
1793
- reviewCommand.command("finalize").description("Auto-set finalDecision for articles with reviewer consensus").requiredOption("--session <id>", "session ID").option("--dry-run", "preview without changes", false).option("--min-reviewers <n>", "minimum agreeing reviewers needed", "1").action(async (options) => {
1867
+ reviewCommand.command("finalize").description("Auto-set finalDecision for articles with reviewer consensus").requiredOption("--session <id>", "session ID").option("--dry-run", "preview without changes", false).option("--min-reviewers <n>", "minimum agreeing reviewers needed", "1").option("--decision <type>", "only finalize this decision type (include or exclude)").action(async (options) => {
1794
1868
  const globalOpts = program.opts();
1795
1869
  try {
1796
1870
  const sessionsDir = await getSessionsDir(globalOpts);
1871
+ if (options.decision && options.decision !== "include" && options.decision !== "exclude") {
1872
+ if (!globalOpts.quiet) {
1873
+ console.error(`Error: --decision must be "include" or "exclude", got "${options.decision}"`);
1874
+ }
1875
+ process.exitCode = EXIT_CODES.GENERAL_ERROR;
1876
+ return;
1877
+ }
1797
1878
  const finalizeOptions = {
1798
1879
  sessionId: options.session,
1799
- ...options.dryRun && { dryRun: options.dryRun }
1880
+ ...options.dryRun && { dryRun: options.dryRun },
1881
+ ...options.decision && { decision: options.decision }
1800
1882
  };
1801
1883
  const minReviewers = parseInt(options.minReviewers, 10);
1802
1884
  if (!Number.isNaN(minReviewers) && minReviewers > 1) {
@@ -1804,7 +1886,10 @@ Examples:
1804
1886
  }
1805
1887
  const result = await executeReviewFinalize(finalizeOptions, sessionsDir);
1806
1888
  if (!globalOpts.quiet) {
1807
- console.log(formatFinalizeOutput(result, { dryRun: options.dryRun }));
1889
+ console.log(formatFinalizeOutput(result, {
1890
+ dryRun: options.dryRun,
1891
+ ...finalizeOptions.decision && { decision: finalizeOptions.decision }
1892
+ }));
1808
1893
  if (!options.dryRun) {
1809
1894
  const statusResult = await executeReviewStatus({ sessionId: options.session }, sessionsDir);
1810
1895
  const suggestion = formatSuggestion(getSuggestion({