@ncukondo/search-hub 0.23.0 → 0.23.1

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.
@@ -14,7 +14,6 @@ export declare const QUERY_SCHEMA_FILENAME = "query.schema.json";
14
14
  * @returns The YAML template string with comments
15
15
  */
16
16
  export declare function generateQueryTemplate(title?: string): string;
17
- export declare const QUERIES_DIR = "queries";
18
17
  export interface WriteQueryTemplateOptions {
19
18
  title: string;
20
19
  output?: string | undefined;
@@ -1 +1 @@
1
- {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../../../src/cli/commands/query/init.ts"],"names":[],"mappings":"AASA;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAYzD;AAED;;;GAGG;AACH,eAAO,MAAM,qBAAqB,sBAAsB,CAAC;AA2DzD;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAI5D;AAED,eAAO,MAAM,WAAW,YAAY,CAAC;AAErC,MAAM,WAAW,yBAAyB;IACxC,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B,MAAM,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAC7B,KAAK,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAC5B,GAAG,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC1B;AAED;;;;;;;GAOG;AACH,wBAAsB,kBAAkB,CAAC,OAAO,EAAE,yBAAyB,GAAG,OAAO,CAAC;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAwChJ"}
1
+ {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../../../src/cli/commands/query/init.ts"],"names":[],"mappings":"AAUA;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAYzD;AAED;;;GAGG;AACH,eAAO,MAAM,qBAAqB,sBAAsB,CAAC;AA2DzD;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAI5D;AAGD,MAAM,WAAW,yBAAyB;IACxC,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B,MAAM,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAC7B,KAAK,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAC5B,GAAG,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC1B;AAED;;;;;;;GAOG;AACH,wBAAsB,kBAAkB,CAAC,OAAO,EAAE,yBAAyB,GAAG,OAAO,CAAC;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAwChJ"}
@@ -1,6 +1,7 @@
1
1
  import { access, mkdir, writeFile } from "node:fs/promises";
2
2
  import { join, dirname } from "node:path";
3
3
  import { generateQueryJSONSchema } from "../../../query/json-schema.js";
4
+ import { getQueriesDir } from "../../../config/paths.js";
4
5
  function sanitizeForFilename(title) {
5
6
  const result = title.trim().toLowerCase().replace(/\s+/g, "-").replace(/[^a-z0-9_-]/g, "").replace(/-+/g, "-").replace(/^-|-$/g, "");
6
7
  if (!result) {
@@ -69,13 +70,12 @@ function generateQueryTemplate(title) {
69
70
  const escaped = title.replace(/"/g, '\\"');
70
71
  return QUERY_TEMPLATE.replace("name: my_search", `name: "${escaped}"`);
71
72
  }
72
- const QUERIES_DIR = "queries";
73
73
  async function writeQueryTemplate(options) {
74
74
  const template = generateQueryTemplate(options.title);
75
75
  if (options.stdout) {
76
76
  return { success: true, message: template };
77
77
  }
78
- const outputPath = options.output ?? join(options.cwd ?? process.cwd(), QUERIES_DIR, `${sanitizeForFilename(options.title)}.yaml`);
78
+ const outputPath = options.output ?? join(getQueriesDir(options.cwd), `${sanitizeForFilename(options.title)}.yaml`);
79
79
  if (!options.force) {
80
80
  try {
81
81
  await access(outputPath);
@@ -98,7 +98,6 @@ async function writeQueryTemplate(options) {
98
98
  };
99
99
  }
100
100
  export {
101
- QUERIES_DIR,
102
101
  QUERY_SCHEMA_FILENAME,
103
102
  generateQueryTemplate,
104
103
  sanitizeForFilename,
@@ -1 +1 @@
1
- {"version":3,"file":"init.js","sources":["../../../../src/cli/commands/query/init.ts"],"sourcesContent":["/**\n * Query init command implementation.\n *\n * Generates a skeleton YAML query file with helpful comments.\n */\nimport { writeFile as fsWriteFile, access, mkdir } from \"node:fs/promises\";\nimport { dirname, join } from \"node:path\";\nimport { generateQueryJSONSchema } from \"../../../query/json-schema.js\";\n\n/**\n * Sanitize a title string into a safe filename (without extension).\n */\nexport function sanitizeForFilename(title: string): string {\n const result = title\n .trim()\n .toLowerCase()\n .replace(/\\s+/g, '-')\n .replace(/[^a-z0-9_-]/g, '')\n .replace(/-+/g, '-')\n .replace(/^-|-$/g, '');\n if (!result) {\n throw new Error('Title produces an empty filename after sanitization');\n }\n return result;\n}\n\n/**\n * The YAML template string with comments preserved.\n * This is a raw string (not generated by a YAML library) so comments are kept.\n */\nexport const QUERY_SCHEMA_FILENAME = \"query.schema.json\";\n\n// prettier-ignore\nconst QUERY_TEMPLATE =\n `# yaml-language-server: $schema=./${QUERY_SCHEMA_FILENAME}\\n` +\n \"name: my_search\\n\" +\n \"description: \\\"\\\"\\n\" +\n \"\\n\" +\n \"query:\\n\" +\n \" - id: concept-1 # Unique block identifier (for provider replacements)\\n\" +\n \" field: title_abstract # title, abstract, title_abstract, author, keyword, all\\n\" +\n \" terms:\\n\" +\n \" keywords:\\n\" +\n \" - \\\"search term 1\\\"\\n\" +\n \" - \\\"search term 2\\\"\\n\" +\n \" # mesh: # PubMed MeSH terms (optional)\\n\" +\n \" # - \\\"MeSH Heading\\\"\\n\" +\n \" # eric: # ERIC Descriptors (optional, ERIC only)\\n\" +\n \" # - \\\"ERIC Descriptor\\\"\\n\" +\n \" exclude: [] # Terms to exclude (NOT operator)\\n\" +\n \" # Tip: Use exclude to filter out false matches from short keywords/acronyms\\n\" +\n \" # exclude:\\n\" +\n \" # - \\\"unwanted term\\\"\\n\" +\n \" # - \\\"irrelevant topic\\\"\\n\" +\n \" operator: OR # How to combine terms within this block\\n\" +\n \"\\n\" +\n \" # Add more blocks — blocks are AND'd together\\n\" +\n \" # - id: concept-2\\n\" +\n \" # field: title_abstract\\n\" +\n \" # terms:\\n\" +\n \" # keywords:\\n\" +\n \" # - \\\"another term\\\"\\n\" +\n \" # operator: OR\\n\" +\n \"\\n\" +\n \"# filters: # Optional: apply to all databases\\n\" +\n \"# year_from: 2020\\n\" +\n \"# year_to: 2026\\n\" +\n \"# language:\\n\" +\n \"# - en\\n\" +\n \"# publication_types:\\n\" +\n \"# exclude:\\n\" +\n \"# - \\\"Review\\\"\\n\" +\n \"# - \\\"Comment\\\"\\n\" +\n \"\\n\" +\n \"# providers: # Optional: per-database block replacements & filter additions\\n\" +\n \"# pubmed:\\n\" +\n \"# replaces:\\n\" +\n \"# concept-1: # Replace block by id\\n\" +\n \"# field: keyword\\n\" +\n \"# terms:\\n\" +\n \"# mesh:\\n\" +\n \"# - \\\"MeSH Heading\\\"\\n\" +\n \"# operator: OR\\n\" +\n \"# adds:\\n\" +\n \"# filters:\\n\" +\n \"# publication_types:\\n\" +\n \"# exclude:\\n\" +\n \"# - \\\"Letter\\\"\\n\";\n\n/**\n * Generate the query template YAML string.\n *\n * @param title - Optional title to set as the query name\n * @returns The YAML template string with comments\n */\nexport function generateQueryTemplate(title?: string): string {\n if (!title) return QUERY_TEMPLATE;\n const escaped = title.replace(/\"/g, '\\\\\"');\n return QUERY_TEMPLATE.replace('name: my_search', `name: \"${escaped}\"`);\n}\n\nexport const QUERIES_DIR = \"queries\";\n\nexport interface WriteQueryTemplateOptions {\n title: string;\n output?: string | undefined;\n stdout?: boolean | undefined;\n force?: boolean | undefined;\n cwd?: string | undefined;\n}\n\n/**\n * Write the query template to a file or return it as a message.\n *\n * Output priority:\n * 1. --stdout → return template as message (no file)\n * 2. -o <path> → write to that path\n * 3. default → write to queries/<sanitized-title>.yaml\n */\nexport async function writeQueryTemplate(options: WriteQueryTemplateOptions): Promise<{ success: boolean; message: string; outputPath?: string }> {\n const template = generateQueryTemplate(options.title);\n\n if (options.stdout) {\n return { success: true, message: template };\n }\n\n // Determine output path\n const outputPath = options.output\n ?? join(options.cwd ?? process.cwd(), QUERIES_DIR, `${sanitizeForFilename(options.title)}.yaml`);\n\n // Check if file exists (unless force is set)\n if (!options.force) {\n try {\n await access(outputPath);\n return {\n success: false,\n message: `File already exists: ${outputPath}. Use --force to overwrite.`,\n };\n } catch {\n // File does not exist, proceed\n }\n }\n\n // Ensure parent directory exists\n await mkdir(dirname(outputPath), { recursive: true });\n\n // Write template\n await fsWriteFile(outputPath, template, \"utf-8\");\n\n // Generate JSON Schema file alongside output\n const schemaPath = join(dirname(outputPath), QUERY_SCHEMA_FILENAME);\n const jsonSchema = generateQueryJSONSchema();\n await fsWriteFile(schemaPath, JSON.stringify(jsonSchema, null, 2) + \"\\n\", \"utf-8\");\n\n return {\n success: true,\n message: `Created: ${outputPath}`,\n outputPath,\n };\n}\n"],"names":["fsWriteFile"],"mappings":";;;AAYO,SAAS,oBAAoB,OAAuB;AACzD,QAAM,SAAS,MACZ,KAAA,EACA,cACA,QAAQ,QAAQ,GAAG,EACnB,QAAQ,gBAAgB,EAAE,EAC1B,QAAQ,OAAO,GAAG,EAClB,QAAQ,UAAU,EAAE;AACvB,MAAI,CAAC,QAAQ;AACX,UAAM,IAAI,MAAM,qDAAqD;AAAA,EACvE;AACA,SAAO;AACT;AAMO,MAAM,wBAAwB;AAGrC,MAAM,iBACJ,qCAAqC,qBAAqB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA6DrD,SAAS,sBAAsB,OAAwB;AAC5D,MAAI,CAAC,MAAO,QAAO;AACnB,QAAM,UAAU,MAAM,QAAQ,MAAM,KAAK;AACzC,SAAO,eAAe,QAAQ,mBAAmB,UAAU,OAAO,GAAG;AACvE;AAEO,MAAM,cAAc;AAkB3B,eAAsB,mBAAmB,SAAyG;AAChJ,QAAM,WAAW,sBAAsB,QAAQ,KAAK;AAEpD,MAAI,QAAQ,QAAQ;AAClB,WAAO,EAAE,SAAS,MAAM,SAAS,SAAA;AAAA,EACnC;AAGA,QAAM,aAAa,QAAQ,UACtB,KAAK,QAAQ,OAAO,QAAQ,IAAA,GAAO,aAAa,GAAG,oBAAoB,QAAQ,KAAK,CAAC,OAAO;AAGjG,MAAI,CAAC,QAAQ,OAAO;AAClB,QAAI;AACF,YAAM,OAAO,UAAU;AACvB,aAAO;AAAA,QACL,SAAS;AAAA,QACT,SAAS,wBAAwB,UAAU;AAAA,MAAA;AAAA,IAE/C,QAAQ;AAAA,IAER;AAAA,EACF;AAGA,QAAM,MAAM,QAAQ,UAAU,GAAG,EAAE,WAAW,MAAM;AAGpD,QAAMA,UAAY,YAAY,UAAU,OAAO;AAG/C,QAAM,aAAa,KAAK,QAAQ,UAAU,GAAG,qBAAqB;AAClE,QAAM,aAAa,wBAAA;AACnB,QAAMA,UAAY,YAAY,KAAK,UAAU,YAAY,MAAM,CAAC,IAAI,MAAM,OAAO;AAEjF,SAAO;AAAA,IACL,SAAS;AAAA,IACT,SAAS,YAAY,UAAU;AAAA,IAC/B;AAAA,EAAA;AAEJ;"}
1
+ {"version":3,"file":"init.js","sources":["../../../../src/cli/commands/query/init.ts"],"sourcesContent":["/**\n * Query init command implementation.\n *\n * Generates a skeleton YAML query file with helpful comments.\n */\nimport { writeFile as fsWriteFile, access, mkdir } from \"node:fs/promises\";\nimport { dirname, join } from \"node:path\";\nimport { generateQueryJSONSchema } from \"../../../query/json-schema.js\";\nimport { getQueriesDir } from \"../../../config/paths.js\";\n\n/**\n * Sanitize a title string into a safe filename (without extension).\n */\nexport function sanitizeForFilename(title: string): string {\n const result = title\n .trim()\n .toLowerCase()\n .replace(/\\s+/g, '-')\n .replace(/[^a-z0-9_-]/g, '')\n .replace(/-+/g, '-')\n .replace(/^-|-$/g, '');\n if (!result) {\n throw new Error('Title produces an empty filename after sanitization');\n }\n return result;\n}\n\n/**\n * The YAML template string with comments preserved.\n * This is a raw string (not generated by a YAML library) so comments are kept.\n */\nexport const QUERY_SCHEMA_FILENAME = \"query.schema.json\";\n\n// prettier-ignore\nconst QUERY_TEMPLATE =\n `# yaml-language-server: $schema=./${QUERY_SCHEMA_FILENAME}\\n` +\n \"name: my_search\\n\" +\n \"description: \\\"\\\"\\n\" +\n \"\\n\" +\n \"query:\\n\" +\n \" - id: concept-1 # Unique block identifier (for provider replacements)\\n\" +\n \" field: title_abstract # title, abstract, title_abstract, author, keyword, all\\n\" +\n \" terms:\\n\" +\n \" keywords:\\n\" +\n \" - \\\"search term 1\\\"\\n\" +\n \" - \\\"search term 2\\\"\\n\" +\n \" # mesh: # PubMed MeSH terms (optional)\\n\" +\n \" # - \\\"MeSH Heading\\\"\\n\" +\n \" # eric: # ERIC Descriptors (optional, ERIC only)\\n\" +\n \" # - \\\"ERIC Descriptor\\\"\\n\" +\n \" exclude: [] # Terms to exclude (NOT operator)\\n\" +\n \" # Tip: Use exclude to filter out false matches from short keywords/acronyms\\n\" +\n \" # exclude:\\n\" +\n \" # - \\\"unwanted term\\\"\\n\" +\n \" # - \\\"irrelevant topic\\\"\\n\" +\n \" operator: OR # How to combine terms within this block\\n\" +\n \"\\n\" +\n \" # Add more blocks — blocks are AND'd together\\n\" +\n \" # - id: concept-2\\n\" +\n \" # field: title_abstract\\n\" +\n \" # terms:\\n\" +\n \" # keywords:\\n\" +\n \" # - \\\"another term\\\"\\n\" +\n \" # operator: OR\\n\" +\n \"\\n\" +\n \"# filters: # Optional: apply to all databases\\n\" +\n \"# year_from: 2020\\n\" +\n \"# year_to: 2026\\n\" +\n \"# language:\\n\" +\n \"# - en\\n\" +\n \"# publication_types:\\n\" +\n \"# exclude:\\n\" +\n \"# - \\\"Review\\\"\\n\" +\n \"# - \\\"Comment\\\"\\n\" +\n \"\\n\" +\n \"# providers: # Optional: per-database block replacements & filter additions\\n\" +\n \"# pubmed:\\n\" +\n \"# replaces:\\n\" +\n \"# concept-1: # Replace block by id\\n\" +\n \"# field: keyword\\n\" +\n \"# terms:\\n\" +\n \"# mesh:\\n\" +\n \"# - \\\"MeSH Heading\\\"\\n\" +\n \"# operator: OR\\n\" +\n \"# adds:\\n\" +\n \"# filters:\\n\" +\n \"# publication_types:\\n\" +\n \"# exclude:\\n\" +\n \"# - \\\"Letter\\\"\\n\";\n\n/**\n * Generate the query template YAML string.\n *\n * @param title - Optional title to set as the query name\n * @returns The YAML template string with comments\n */\nexport function generateQueryTemplate(title?: string): string {\n if (!title) return QUERY_TEMPLATE;\n const escaped = title.replace(/\"/g, '\\\\\"');\n return QUERY_TEMPLATE.replace('name: my_search', `name: \"${escaped}\"`);\n}\n\n\nexport interface WriteQueryTemplateOptions {\n title: string;\n output?: string | undefined;\n stdout?: boolean | undefined;\n force?: boolean | undefined;\n cwd?: string | undefined;\n}\n\n/**\n * Write the query template to a file or return it as a message.\n *\n * Output priority:\n * 1. --stdout → return template as message (no file)\n * 2. -o <path> → write to that path\n * 3. default → write to queries/<sanitized-title>.yaml\n */\nexport async function writeQueryTemplate(options: WriteQueryTemplateOptions): Promise<{ success: boolean; message: string; outputPath?: string }> {\n const template = generateQueryTemplate(options.title);\n\n if (options.stdout) {\n return { success: true, message: template };\n }\n\n // Determine output path\n const outputPath = options.output\n ?? join(getQueriesDir(options.cwd), `${sanitizeForFilename(options.title)}.yaml`);\n\n // Check if file exists (unless force is set)\n if (!options.force) {\n try {\n await access(outputPath);\n return {\n success: false,\n message: `File already exists: ${outputPath}. Use --force to overwrite.`,\n };\n } catch {\n // File does not exist, proceed\n }\n }\n\n // Ensure parent directory exists\n await mkdir(dirname(outputPath), { recursive: true });\n\n // Write template\n await fsWriteFile(outputPath, template, \"utf-8\");\n\n // Generate JSON Schema file alongside output\n const schemaPath = join(dirname(outputPath), QUERY_SCHEMA_FILENAME);\n const jsonSchema = generateQueryJSONSchema();\n await fsWriteFile(schemaPath, JSON.stringify(jsonSchema, null, 2) + \"\\n\", \"utf-8\");\n\n return {\n success: true,\n message: `Created: ${outputPath}`,\n outputPath,\n };\n}\n"],"names":["fsWriteFile"],"mappings":";;;;AAaO,SAAS,oBAAoB,OAAuB;AACzD,QAAM,SAAS,MACZ,KAAA,EACA,cACA,QAAQ,QAAQ,GAAG,EACnB,QAAQ,gBAAgB,EAAE,EAC1B,QAAQ,OAAO,GAAG,EAClB,QAAQ,UAAU,EAAE;AACvB,MAAI,CAAC,QAAQ;AACX,UAAM,IAAI,MAAM,qDAAqD;AAAA,EACvE;AACA,SAAO;AACT;AAMO,MAAM,wBAAwB;AAGrC,MAAM,iBACJ,qCAAqC,qBAAqB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA6DrD,SAAS,sBAAsB,OAAwB;AAC5D,MAAI,CAAC,MAAO,QAAO;AACnB,QAAM,UAAU,MAAM,QAAQ,MAAM,KAAK;AACzC,SAAO,eAAe,QAAQ,mBAAmB,UAAU,OAAO,GAAG;AACvE;AAmBA,eAAsB,mBAAmB,SAAyG;AAChJ,QAAM,WAAW,sBAAsB,QAAQ,KAAK;AAEpD,MAAI,QAAQ,QAAQ;AAClB,WAAO,EAAE,SAAS,MAAM,SAAS,SAAA;AAAA,EACnC;AAGA,QAAM,aAAa,QAAQ,UACtB,KAAK,cAAc,QAAQ,GAAG,GAAG,GAAG,oBAAoB,QAAQ,KAAK,CAAC,OAAO;AAGlF,MAAI,CAAC,QAAQ,OAAO;AAClB,QAAI;AACF,YAAM,OAAO,UAAU;AACvB,aAAO;AAAA,QACL,SAAS;AAAA,QACT,SAAS,wBAAwB,UAAU;AAAA,MAAA;AAAA,IAE/C,QAAQ;AAAA,IAER;AAAA,EACF;AAGA,QAAM,MAAM,QAAQ,UAAU,GAAG,EAAE,WAAW,MAAM;AAGpD,QAAMA,UAAY,YAAY,UAAU,OAAO;AAG/C,QAAM,aAAa,KAAK,QAAQ,UAAU,GAAG,qBAAqB;AAClE,QAAM,aAAa,wBAAA;AACnB,QAAMA,UAAY,YAAY,KAAK,UAAU,YAAY,MAAM,CAAC,IAAI,MAAM,OAAO;AAEjF,SAAO;AAAA,IACL,SAAS;AAAA,IACT,SAAS,YAAY,UAAU;AAAA,IAC/B;AAAA,EAAA;AAEJ;"}
@@ -1 +1 @@
1
- {"version":3,"file":"resolve.d.ts","sourceRoot":"","sources":["../../../../src/cli/commands/query/resolve.ts"],"names":[],"mappings":"AAYA,qBAAa,aAAc,SAAQ,KAAK;gBAC1B,IAAI,EAAE,MAAM;CAIzB;AAiBD,wBAAsB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CA0CnE"}
1
+ {"version":3,"file":"resolve.d.ts","sourceRoot":"","sources":["../../../../src/cli/commands/query/resolve.ts"],"names":[],"mappings":"AAcA,qBAAa,aAAc,SAAQ,KAAK;gBAC1B,IAAI,EAAE,MAAM;CAIzB;AAiBD,wBAAsB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CA2CnE"}
@@ -1,4 +1,6 @@
1
1
  import { stat } from "node:fs/promises";
2
+ import { relative } from "node:path";
3
+ import { getQueriesDir } from "../../../config/paths.js";
2
4
  class NotAFileError extends Error {
3
5
  constructor(path) {
4
6
  super(`Path is not a file: ${path}`);
@@ -31,14 +33,15 @@ async function resolveQueryFile(arg) {
31
33
  return withExt;
32
34
  }
33
35
  }
36
+ const queriesDir = relative(process.cwd(), getQueriesDir());
34
37
  const basename = arg.endsWith(".yaml") || arg.endsWith(".yml") ? arg : `${arg}.yaml`;
35
- const inQueries = `queries/${basename}`;
38
+ const inQueries = `${queriesDir}/${basename}`;
36
39
  candidates.push(inQueries);
37
40
  if (await isFile(inQueries)) {
38
41
  return inQueries;
39
42
  }
40
43
  if (!arg.endsWith(".yaml") && !arg.endsWith(".yml")) {
41
- const inQueriesYml = `queries/${arg}.yml`;
44
+ const inQueriesYml = `${queriesDir}/${arg}.yml`;
42
45
  candidates.push(inQueriesYml);
43
46
  if (await isFile(inQueriesYml)) {
44
47
  return inQueriesYml;
@@ -1 +1 @@
1
- {"version":3,"file":"resolve.js","sources":["../../../../src/cli/commands/query/resolve.ts"],"sourcesContent":["/**\n * Smart query file resolution.\n *\n * Resolution order:\n * 1. Exact path exists → use it\n * 2. <arg>.yaml exists → use it\n * 3. queries/<arg>.yaml exists → use it\n * 4. queries/<arg>.yml exists → use it\n * 5. Error with tried paths\n */\nimport { stat } from 'node:fs/promises';\n\nexport class NotAFileError extends Error {\n constructor(path: string) {\n super(`Path is not a file: ${path}`);\n this.name = 'NotAFileError';\n }\n}\n\nasync function isFile(path: string): Promise<boolean> {\n try {\n const s = await stat(path);\n if (!s.isFile()) {\n throw new NotAFileError(path);\n }\n return true;\n } catch (error) {\n if (error instanceof NotAFileError) {\n throw error;\n }\n return false;\n }\n}\n\nexport async function resolveQueryFile(arg: string): Promise<string> {\n // 1. Exact path\n if (await isFile(arg)) {\n return arg;\n }\n\n const candidates: string[] = [];\n\n // 2. arg + .yaml (skip if already ends with .yaml)\n if (!arg.endsWith('.yaml') && !arg.endsWith('.yml')) {\n const withExt = `${arg}.yaml`;\n candidates.push(withExt);\n if (await isFile(withExt)) {\n return withExt;\n }\n }\n\n // 3. queries/<arg>.yaml\n const basename = arg.endsWith('.yaml') || arg.endsWith('.yml') ? arg : `${arg}.yaml`;\n const inQueries = `queries/${basename}`;\n candidates.push(inQueries);\n if (await isFile(inQueries)) {\n return inQueries;\n }\n\n // 4. queries/<arg>.yml (skip if arg already has extension)\n if (!arg.endsWith('.yaml') && !arg.endsWith('.yml')) {\n const inQueriesYml = `queries/${arg}.yml`;\n candidates.push(inQueriesYml);\n if (await isFile(inQueriesYml)) {\n return inQueriesYml;\n }\n }\n\n // 5. Error\n const tried = [`./${arg}`, ...candidates.map(c => `./${c}`)];\n throw new Error(\n `Query file not found: \"${arg}\"\\n` +\n ` Tried:\\n` +\n tried.map(p => ` ${p}`).join('\\n') + '\\n' +\n ` Create a new query: search-hub query init \"${arg}\"`\n );\n}\n"],"names":[],"mappings":";AAYO,MAAM,sBAAsB,MAAM;AAAA,EACvC,YAAY,MAAc;AACxB,UAAM,uBAAuB,IAAI,EAAE;AACnC,SAAK,OAAO;AAAA,EACd;AACF;AAEA,eAAe,OAAO,MAAgC;AACpD,MAAI;AACF,UAAM,IAAI,MAAM,KAAK,IAAI;AACzB,QAAI,CAAC,EAAE,UAAU;AACf,YAAM,IAAI,cAAc,IAAI;AAAA,IAC9B;AACA,WAAO;AAAA,EACT,SAAS,OAAO;AACd,QAAI,iBAAiB,eAAe;AAClC,YAAM;AAAA,IACR;AACA,WAAO;AAAA,EACT;AACF;AAEA,eAAsB,iBAAiB,KAA8B;AAEnE,MAAI,MAAM,OAAO,GAAG,GAAG;AACrB,WAAO;AAAA,EACT;AAEA,QAAM,aAAuB,CAAA;AAG7B,MAAI,CAAC,IAAI,SAAS,OAAO,KAAK,CAAC,IAAI,SAAS,MAAM,GAAG;AACnD,UAAM,UAAU,GAAG,GAAG;AACtB,eAAW,KAAK,OAAO;AACvB,QAAI,MAAM,OAAO,OAAO,GAAG;AACzB,aAAO;AAAA,IACT;AAAA,EACF;AAGA,QAAM,WAAW,IAAI,SAAS,OAAO,KAAK,IAAI,SAAS,MAAM,IAAI,MAAM,GAAG,GAAG;AAC7E,QAAM,YAAY,WAAW,QAAQ;AACrC,aAAW,KAAK,SAAS;AACzB,MAAI,MAAM,OAAO,SAAS,GAAG;AAC3B,WAAO;AAAA,EACT;AAGA,MAAI,CAAC,IAAI,SAAS,OAAO,KAAK,CAAC,IAAI,SAAS,MAAM,GAAG;AACnD,UAAM,eAAe,WAAW,GAAG;AACnC,eAAW,KAAK,YAAY;AAC5B,QAAI,MAAM,OAAO,YAAY,GAAG;AAC9B,aAAO;AAAA,IACT;AAAA,EACF;AAGA,QAAM,QAAQ,CAAC,KAAK,GAAG,IAAI,GAAG,WAAW,IAAI,CAAA,MAAK,KAAK,CAAC,EAAE,CAAC;AAC3D,QAAM,IAAI;AAAA,IACR,0BAA0B,GAAG;AAAA;AAAA,IAE7B,MAAM,IAAI,CAAA,MAAK,OAAO,CAAC,EAAE,EAAE,KAAK,IAAI,IAAI;AAAA,+CACQ,GAAG;AAAA,EAAA;AAEvD;"}
1
+ {"version":3,"file":"resolve.js","sources":["../../../../src/cli/commands/query/resolve.ts"],"sourcesContent":["/**\n * Smart query file resolution.\n *\n * Resolution order:\n * 1. Exact path exists → use it\n * 2. <arg>.yaml exists → use it\n * 3. .search-hub/queries/<arg>.yaml exists → use it\n * 4. .search-hub/queries/<arg>.yml exists → use it\n * 5. Error with tried paths\n */\nimport { stat } from 'node:fs/promises';\nimport { relative } from 'node:path';\nimport { getQueriesDir } from '../../../config/paths.js';\n\nexport class NotAFileError extends Error {\n constructor(path: string) {\n super(`Path is not a file: ${path}`);\n this.name = 'NotAFileError';\n }\n}\n\nasync function isFile(path: string): Promise<boolean> {\n try {\n const s = await stat(path);\n if (!s.isFile()) {\n throw new NotAFileError(path);\n }\n return true;\n } catch (error) {\n if (error instanceof NotAFileError) {\n throw error;\n }\n return false;\n }\n}\n\nexport async function resolveQueryFile(arg: string): Promise<string> {\n // 1. Exact path\n if (await isFile(arg)) {\n return arg;\n }\n\n const candidates: string[] = [];\n\n // 2. arg + .yaml (skip if already ends with .yaml)\n if (!arg.endsWith('.yaml') && !arg.endsWith('.yml')) {\n const withExt = `${arg}.yaml`;\n candidates.push(withExt);\n if (await isFile(withExt)) {\n return withExt;\n }\n }\n\n // 3. .search-hub/queries/<arg>.yaml\n const queriesDir = relative(process.cwd(), getQueriesDir());\n const basename = arg.endsWith('.yaml') || arg.endsWith('.yml') ? arg : `${arg}.yaml`;\n const inQueries = `${queriesDir}/${basename}`;\n candidates.push(inQueries);\n if (await isFile(inQueries)) {\n return inQueries;\n }\n\n // 4. .search-hub/queries/<arg>.yml (skip if arg already has extension)\n if (!arg.endsWith('.yaml') && !arg.endsWith('.yml')) {\n const inQueriesYml = `${queriesDir}/${arg}.yml`;\n candidates.push(inQueriesYml);\n if (await isFile(inQueriesYml)) {\n return inQueriesYml;\n }\n }\n\n // 5. Error\n const tried = [`./${arg}`, ...candidates.map(c => `./${c}`)];\n throw new Error(\n `Query file not found: \"${arg}\"\\n` +\n ` Tried:\\n` +\n tried.map(p => ` ${p}`).join('\\n') + '\\n' +\n ` Create a new query: search-hub query init \"${arg}\"`\n );\n}\n"],"names":[],"mappings":";;;AAcO,MAAM,sBAAsB,MAAM;AAAA,EACvC,YAAY,MAAc;AACxB,UAAM,uBAAuB,IAAI,EAAE;AACnC,SAAK,OAAO;AAAA,EACd;AACF;AAEA,eAAe,OAAO,MAAgC;AACpD,MAAI;AACF,UAAM,IAAI,MAAM,KAAK,IAAI;AACzB,QAAI,CAAC,EAAE,UAAU;AACf,YAAM,IAAI,cAAc,IAAI;AAAA,IAC9B;AACA,WAAO;AAAA,EACT,SAAS,OAAO;AACd,QAAI,iBAAiB,eAAe;AAClC,YAAM;AAAA,IACR;AACA,WAAO;AAAA,EACT;AACF;AAEA,eAAsB,iBAAiB,KAA8B;AAEnE,MAAI,MAAM,OAAO,GAAG,GAAG;AACrB,WAAO;AAAA,EACT;AAEA,QAAM,aAAuB,CAAA;AAG7B,MAAI,CAAC,IAAI,SAAS,OAAO,KAAK,CAAC,IAAI,SAAS,MAAM,GAAG;AACnD,UAAM,UAAU,GAAG,GAAG;AACtB,eAAW,KAAK,OAAO;AACvB,QAAI,MAAM,OAAO,OAAO,GAAG;AACzB,aAAO;AAAA,IACT;AAAA,EACF;AAGA,QAAM,aAAa,SAAS,QAAQ,IAAA,GAAO,eAAe;AAC1D,QAAM,WAAW,IAAI,SAAS,OAAO,KAAK,IAAI,SAAS,MAAM,IAAI,MAAM,GAAG,GAAG;AAC7E,QAAM,YAAY,GAAG,UAAU,IAAI,QAAQ;AAC3C,aAAW,KAAK,SAAS;AACzB,MAAI,MAAM,OAAO,SAAS,GAAG;AAC3B,WAAO;AAAA,EACT;AAGA,MAAI,CAAC,IAAI,SAAS,OAAO,KAAK,CAAC,IAAI,SAAS,MAAM,GAAG;AACnD,UAAM,eAAe,GAAG,UAAU,IAAI,GAAG;AACzC,eAAW,KAAK,YAAY;AAC5B,QAAI,MAAM,OAAO,YAAY,GAAG;AAC9B,aAAO;AAAA,IACT;AAAA,EACF;AAGA,QAAM,QAAQ,CAAC,KAAK,GAAG,IAAI,GAAG,WAAW,IAAI,CAAA,MAAK,KAAK,CAAC,EAAE,CAAC;AAC3D,QAAM,IAAI;AAAA,IACR,0BAA0B,GAAG;AAAA;AAAA,IAE7B,MAAM,IAAI,CAAA,MAAK,OAAO,CAAC,EAAE,EAAE,KAAK,IAAI,IAAI;AAAA,+CACQ,GAAG;AAAA,EAAA;AAEvD;"}
package/dist/cli/index.js CHANGED
@@ -494,7 +494,7 @@ Examples:
494
494
  });
495
495
  queryCommand.command("init").description("Generate a template query YAML file").argument("<title>", "query title (used for name field and filename)").option("-o, --output <path>", "write to specific file path").option("--stdout", "output to stdout instead of file").option("--force", "overwrite existing file", false).addHelpText("after", `
496
496
  Examples:
497
- $ search-hub query init "WBA pain mechanisms" # → queries/wba-pain-mechanisms.yaml
497
+ $ search-hub query init "WBA pain mechanisms" # → .search-hub/queries/wba-pain-mechanisms.yaml
498
498
  $ search-hub query init "WBA pain" -o ./custom-path.yaml # Custom output path
499
499
  $ search-hub query init "WBA pain" --stdout # Print to stdout`).action(async (title, options) => {
500
500
  const globalOpts = program.opts();