@ncukondo/search-hub 0.22.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.
Files changed (37) hide show
  1. package/README.md +14 -7
  2. package/dist/cli/commands/config.d.ts +53 -0
  3. package/dist/cli/commands/config.d.ts.map +1 -1
  4. package/dist/cli/commands/config.js +62 -0
  5. package/dist/cli/commands/config.js.map +1 -1
  6. package/dist/cli/commands/fulltext/index.js +1 -0
  7. package/dist/cli/commands/fulltext/index.js.map +1 -1
  8. package/dist/cli/commands/init.d.ts +12 -17
  9. package/dist/cli/commands/init.d.ts.map +1 -1
  10. package/dist/cli/commands/init.js +116 -76
  11. package/dist/cli/commands/init.js.map +1 -1
  12. package/dist/cli/commands/query/init.d.ts +0 -1
  13. package/dist/cli/commands/query/init.d.ts.map +1 -1
  14. package/dist/cli/commands/query/init.js +2 -3
  15. package/dist/cli/commands/query/init.js.map +1 -1
  16. package/dist/cli/commands/query/resolve.d.ts.map +1 -1
  17. package/dist/cli/commands/query/resolve.js +5 -2
  18. package/dist/cli/commands/query/resolve.js.map +1 -1
  19. package/dist/cli/index.d.ts.map +1 -1
  20. package/dist/cli/index.js +183 -23
  21. package/dist/cli/index.js.map +1 -1
  22. package/dist/cli/suggestions/rules.js +1 -1
  23. package/dist/cli/suggestions/rules.js.map +1 -1
  24. package/dist/config/index.d.ts +1 -0
  25. package/dist/config/index.d.ts.map +1 -1
  26. package/dist/config/loader.d.ts +4 -2
  27. package/dist/config/loader.d.ts.map +1 -1
  28. package/dist/config/loader.js +7 -6
  29. package/dist/config/loader.js.map +1 -1
  30. package/dist/config/paths.d.ts +21 -0
  31. package/dist/config/paths.d.ts.map +1 -1
  32. package/dist/config/paths.js +28 -5
  33. package/dist/config/paths.js.map +1 -1
  34. package/dist/index.js +6 -0
  35. package/dist/index.js.map +1 -1
  36. package/dist/package.json.js +1 -1
  37. package/package.json +1 -1
@@ -3,8 +3,27 @@ import { join } from "node:path";
3
3
  import { stringify } from "@iarna/toml";
4
4
  import "../../config/schema.js";
5
5
  import { getDefaultConfig } from "../../config/defaults.js";
6
- import { getConfigDir, getDataDir } from "../../config/paths.js";
6
+ import { getConfigDir, getProjectDir } from "../../config/paths.js";
7
7
  import "node:os";
8
+ const FULL_PROVIDERS = ["pubmed", "eric", "arxiv", "scopus"];
9
+ const MINIMAL_PROVIDERS = ["wos", "embase"];
10
+ function buildProvidersToml(config) {
11
+ const providers = {};
12
+ for (const name of FULL_PROVIDERS) {
13
+ const p = config.providers[name];
14
+ providers[name] = {
15
+ enabled: p.enabled,
16
+ rate_limit: p.rate_limit,
17
+ timeout: p.timeout,
18
+ retries: p.retries,
19
+ max_results: p.max_results
20
+ };
21
+ }
22
+ for (const name of MINIMAL_PROVIDERS) {
23
+ providers[name] = { enabled: config.providers[name].enabled };
24
+ }
25
+ return providers;
26
+ }
8
27
  async function exists(path) {
9
28
  try {
10
29
  await access(path, constants.F_OK);
@@ -13,11 +32,20 @@ async function exists(path) {
13
32
  return false;
14
33
  }
15
34
  }
16
- function configToToml(config) {
35
+ function localConfigToToml(config) {
17
36
  return {
18
- session: {
19
- directory: config.session.directory
20
- },
37
+ providers: buildProvidersToml(config),
38
+ integration: {
39
+ reference_manager: {
40
+ enabled: config.integration.reference_manager.enabled,
41
+ command: config.integration.reference_manager.command,
42
+ auto_register: config.integration.reference_manager.auto_register
43
+ }
44
+ }
45
+ };
46
+ }
47
+ function generateGlobalConfigContent(config) {
48
+ const tomlObj = {
21
49
  log: {
22
50
  level: config.log.level
23
51
  },
@@ -25,55 +53,7 @@ function configToToml(config) {
25
53
  color: config.output.color,
26
54
  progress_bar: config.output.progress_bar
27
55
  },
28
- providers: {
29
- pubmed: {
30
- enabled: config.providers.pubmed.enabled,
31
- api_key: config.providers.pubmed.api_key ?? "",
32
- email: config.providers.pubmed.email ?? "",
33
- rate_limit: config.providers.pubmed.rate_limit,
34
- timeout: config.providers.pubmed.timeout,
35
- retries: config.providers.pubmed.retries,
36
- max_results: config.providers.pubmed.max_results
37
- },
38
- eric: {
39
- enabled: config.providers.eric.enabled,
40
- rate_limit: config.providers.eric.rate_limit,
41
- timeout: config.providers.eric.timeout,
42
- retries: config.providers.eric.retries,
43
- max_results: config.providers.eric.max_results
44
- },
45
- arxiv: {
46
- enabled: config.providers.arxiv.enabled,
47
- rate_limit: config.providers.arxiv.rate_limit,
48
- timeout: config.providers.arxiv.timeout,
49
- retries: config.providers.arxiv.retries,
50
- max_results: config.providers.arxiv.max_results
51
- },
52
- scopus: {
53
- enabled: config.providers.scopus.enabled,
54
- api_key: config.providers.scopus.api_key ?? "",
55
- inst_token: config.providers.scopus.inst_token ?? "",
56
- rate_limit: config.providers.scopus.rate_limit,
57
- timeout: config.providers.scopus.timeout,
58
- retries: config.providers.scopus.retries,
59
- max_results: config.providers.scopus.max_results
60
- },
61
- wos: {
62
- enabled: config.providers.wos.enabled,
63
- api_key: config.providers.wos.api_key ?? "",
64
- rate_limit: config.providers.wos.rate_limit,
65
- timeout: config.providers.wos.timeout,
66
- retries: config.providers.wos.retries,
67
- max_results: config.providers.wos.max_results
68
- },
69
- embase: {
70
- enabled: config.providers.embase.enabled,
71
- rate_limit: config.providers.embase.rate_limit,
72
- timeout: config.providers.embase.timeout,
73
- retries: config.providers.embase.retries,
74
- max_results: config.providers.embase.max_results
75
- }
76
- },
56
+ providers: buildProvidersToml(config),
77
57
  integration: {
78
58
  reference_manager: {
79
59
  enabled: config.integration.reference_manager.enabled,
@@ -82,54 +62,114 @@ function configToToml(config) {
82
62
  }
83
63
  }
84
64
  };
85
- }
86
- function generateConfigContent(config) {
87
- const tomlObj = configToToml(config);
88
- const header = `# search-hub configuration file
65
+ const header = `# search-hub global configuration
89
66
  # See: https://github.com/search-hub/search-hub for documentation
67
+ #
68
+ # [session]
69
+ # directory = "" # Override default session directory (empty = platform default)
70
+
71
+ `;
72
+ const tomlContent = stringify(tomlObj);
73
+ const lines = tomlContent.split("\n");
74
+ const result = [];
75
+ for (const line of lines) {
76
+ result.push(line);
77
+ if (line.startsWith("[providers.pubmed]")) {
78
+ result.push('# api_key = "" # Optional but recommended (NCBI E-utilities)');
79
+ result.push('# email = "" # Required by NCBI for tracking');
80
+ } else if (line.startsWith("[providers.scopus]")) {
81
+ result.push('# api_key = "" # Required for Scopus access');
82
+ result.push('# inst_token = "" # Optional institutional token');
83
+ } else if (line.startsWith("[providers.wos]")) {
84
+ result.push('# api_key = "" # Required for Web of Science');
85
+ }
86
+ }
87
+ return header + result.join("\n");
88
+ }
89
+ function generateLocalConfigContent(config) {
90
+ const tomlObj = localConfigToToml(config);
91
+ const header = `# search-hub project configuration
92
+ # Project-specific overrides (no secrets - use env vars or global config for API keys)
90
93
 
91
94
  `;
92
95
  return header + stringify(tomlObj);
93
96
  }
94
- async function init(options = {}) {
95
- const {
96
- configDir = getConfigDir(),
97
- dataDir = getDataDir(),
98
- force = false
99
- } = options;
100
- const configPath = join(configDir, "config.toml");
101
- const sessionsDir = join(dataDir, "sessions");
102
- const queriesDir = join(dataDir, "queries");
97
+ async function initLocal(directory, force) {
98
+ const projectDir = getProjectDir(directory);
99
+ const configPath = join(projectDir, "config.toml");
100
+ const sessionsDir = join(projectDir, "sessions");
101
+ const queriesDir = join(projectDir, "queries");
103
102
  const result = {
104
103
  success: false,
105
104
  configPath,
106
- sessionsDir,
107
- configDir,
108
- dataDir
105
+ projectDir
109
106
  };
110
- if (await exists(configDir)) {
107
+ if (await exists(projectDir)) {
111
108
  if (!force) {
112
109
  return {
113
110
  ...result,
114
111
  alreadyExists: true,
115
- message: `Configuration directory already exists at ${configDir}. Use --force to overwrite.`
112
+ message: `Project directory already exists at ${projectDir}. Use --force to overwrite.`
116
113
  };
117
114
  }
118
115
  result.overwritten = true;
119
116
  }
120
- await mkdir(configDir, { recursive: true });
117
+ await mkdir(projectDir, { recursive: true });
121
118
  await mkdir(sessionsDir, { recursive: true });
122
119
  await mkdir(queriesDir, { recursive: true });
123
120
  const defaultConfig = getDefaultConfig();
124
- defaultConfig.session.directory = sessionsDir;
125
- const configContent = generateConfigContent(defaultConfig);
121
+ const configContent = generateLocalConfigContent(defaultConfig);
126
122
  await writeFile(configPath, configContent, "utf-8");
127
123
  return {
128
124
  ...result,
129
125
  success: true,
130
- message: result.overwritten ? `Configuration overwritten at ${configDir}` : `Configuration created at ${configDir}`
126
+ message: result.overwritten ? `Project re-initialized at ${projectDir}` : `Project initialized at ${projectDir}`,
127
+ hints: [
128
+ "Set up global credentials: search-hub init --global",
129
+ "Or use environment variables: see search-hub config --env-vars",
130
+ "API keys can also be set via .env file in the project root"
131
+ ]
131
132
  };
132
133
  }
134
+ async function initGlobal(configDir, force) {
135
+ const configPath = join(configDir, "config.toml");
136
+ const result = {
137
+ success: false,
138
+ configPath
139
+ };
140
+ if (await exists(configDir)) {
141
+ if (!force) {
142
+ return {
143
+ ...result,
144
+ alreadyExists: true,
145
+ message: `Global configuration already exists at ${configDir}. Use --force to overwrite.`
146
+ };
147
+ }
148
+ result.overwritten = true;
149
+ }
150
+ await mkdir(configDir, { recursive: true });
151
+ const defaultConfig = getDefaultConfig();
152
+ const configContent = generateGlobalConfigContent(defaultConfig);
153
+ await writeFile(configPath, configContent, "utf-8");
154
+ return {
155
+ ...result,
156
+ success: true,
157
+ message: result.overwritten ? `Global configuration overwritten at ${configDir}` : `Global configuration created at ${configDir}`,
158
+ hints: [
159
+ `Edit credentials: search-hub config --global set providers.pubmed.api_key <key>`,
160
+ `Or edit directly: ${configPath}`
161
+ ]
162
+ };
163
+ }
164
+ async function init(options = {}) {
165
+ const { force = false } = options;
166
+ if (options.global) {
167
+ const configDir = options.configDir ?? getConfigDir();
168
+ return initGlobal(configDir, force);
169
+ }
170
+ const directory = options.directory ?? process.cwd();
171
+ return initLocal(directory, force);
172
+ }
133
173
  export {
134
174
  init
135
175
  };
@@ -1 +1 @@
1
- {"version":3,"file":"init.js","sources":["../../../src/cli/commands/init.ts"],"sourcesContent":["import { mkdir, writeFile, access, constants } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { stringify as stringifyToml } from '@iarna/toml';\nimport { getDefaultConfig } from '../../config/index.js';\nimport { getConfigDir, getDataDir } from '../../config/paths.js';\nimport type { Config } from '../../config/index.js';\n\n/**\n * Options for the init command.\n */\nexport interface InitOptions {\n /** Config directory (defaults to platform-specific via getConfigDir()) */\n configDir?: string;\n /** Data directory (defaults to platform-specific via getDataDir()) */\n dataDir?: string;\n /** Force overwrite if directory already exists */\n force?: boolean;\n}\n\n/**\n * Result of the init command.\n */\nexport interface InitResult {\n /** Whether initialization was successful */\n success: boolean;\n /** Path to the created config file */\n configPath: string;\n /** Path to the sessions directory */\n sessionsDir: string;\n /** Path to the config directory */\n configDir: string;\n /** Path to the data directory */\n dataDir: string;\n /** Whether files already existed (only when success=false) */\n alreadyExists?: boolean;\n /** Whether existing files were overwritten (only when force=true) */\n overwritten?: boolean;\n /** Message describing the result */\n message?: string;\n}\n\n/**\n * Check if a file or directory exists.\n */\nasync function exists(path: string): Promise<boolean> {\n try {\n await access(path, constants.F_OK);\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Convert Config to TOML-compatible object.\n * Removes undefined values and converts to the expected format.\n */\nfunction configToToml(config: Config): Record<string, unknown> {\n return {\n session: {\n directory: config.session.directory,\n },\n log: {\n level: config.log.level,\n },\n output: {\n color: config.output.color,\n progress_bar: config.output.progress_bar,\n },\n providers: {\n pubmed: {\n enabled: config.providers.pubmed.enabled,\n api_key: config.providers.pubmed.api_key ?? '',\n email: config.providers.pubmed.email ?? '',\n rate_limit: config.providers.pubmed.rate_limit,\n timeout: config.providers.pubmed.timeout,\n retries: config.providers.pubmed.retries,\n max_results: config.providers.pubmed.max_results,\n },\n eric: {\n enabled: config.providers.eric.enabled,\n rate_limit: config.providers.eric.rate_limit,\n timeout: config.providers.eric.timeout,\n retries: config.providers.eric.retries,\n max_results: config.providers.eric.max_results,\n },\n arxiv: {\n enabled: config.providers.arxiv.enabled,\n rate_limit: config.providers.arxiv.rate_limit,\n timeout: config.providers.arxiv.timeout,\n retries: config.providers.arxiv.retries,\n max_results: config.providers.arxiv.max_results,\n },\n scopus: {\n enabled: config.providers.scopus.enabled,\n api_key: config.providers.scopus.api_key ?? '',\n inst_token: config.providers.scopus.inst_token ?? '',\n rate_limit: config.providers.scopus.rate_limit,\n timeout: config.providers.scopus.timeout,\n retries: config.providers.scopus.retries,\n max_results: config.providers.scopus.max_results,\n },\n wos: {\n enabled: config.providers.wos.enabled,\n api_key: config.providers.wos.api_key ?? '',\n rate_limit: config.providers.wos.rate_limit,\n timeout: config.providers.wos.timeout,\n retries: config.providers.wos.retries,\n max_results: config.providers.wos.max_results,\n },\n embase: {\n enabled: config.providers.embase.enabled,\n rate_limit: config.providers.embase.rate_limit,\n timeout: config.providers.embase.timeout,\n retries: config.providers.embase.retries,\n max_results: config.providers.embase.max_results,\n },\n },\n integration: {\n reference_manager: {\n enabled: config.integration.reference_manager.enabled,\n command: config.integration.reference_manager.command,\n auto_register: config.integration.reference_manager.auto_register,\n },\n },\n };\n}\n\n/**\n * Generate TOML config file content with comments.\n */\nfunction generateConfigContent(config: Config): string {\n const tomlObj = configToToml(config);\n const header = `# search-hub configuration file\n# See: https://github.com/search-hub/search-hub for documentation\n\n`;\n return header + stringifyToml(tomlObj as Parameters<typeof stringifyToml>[0]);\n}\n\n/**\n * Initialize the search-hub configuration directory.\n *\n * Creates:\n * - Config directory with config.toml\n * - Data directory with sessions/ subdirectory\n *\n * On Linux (XDG):\n * - ~/.config/search-hub/config.toml\n * - ~/.local/share/search-hub/sessions/\n */\nexport async function init(options: InitOptions = {}): Promise<InitResult> {\n const {\n configDir = getConfigDir(),\n dataDir = getDataDir(),\n force = false,\n } = options;\n\n const configPath = join(configDir, 'config.toml');\n const sessionsDir = join(dataDir, 'sessions');\n const queriesDir = join(dataDir, 'queries');\n\n const result: InitResult = {\n success: false,\n configPath,\n sessionsDir,\n configDir,\n dataDir,\n };\n\n // Check if config directory already exists\n if (await exists(configDir)) {\n if (!force) {\n return {\n ...result,\n alreadyExists: true,\n message: `Configuration directory already exists at ${configDir}. Use --force to overwrite.`,\n };\n }\n result.overwritten = true;\n }\n\n // Create directories\n await mkdir(configDir, { recursive: true });\n await mkdir(sessionsDir, { recursive: true });\n await mkdir(queriesDir, { recursive: true });\n\n // Generate and write config file\n // Use the default sessions directory for the saved config\n const defaultConfig = getDefaultConfig();\n // Set session.directory to the actual sessions path for the config file\n defaultConfig.session.directory = sessionsDir;\n const configContent = generateConfigContent(defaultConfig);\n await writeFile(configPath, configContent, 'utf-8');\n\n return {\n ...result,\n success: true,\n message: result.overwritten\n ? `Configuration overwritten at ${configDir}`\n : `Configuration created at ${configDir}`,\n };\n}\n"],"names":["stringifyToml"],"mappings":"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"}
1
+ {"version":3,"file":"init.js","sources":["../../../src/cli/commands/init.ts"],"sourcesContent":["import { mkdir, writeFile, access, constants } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { stringify as stringifyToml } from '@iarna/toml';\nimport { getDefaultConfig } from '../../config/index.js';\nimport { getConfigDir, getProjectDir } from '../../config/paths.js';\nimport type { Config, ProviderConfig } from '../../config/index.js';\n\n/** Provider names that carry full settings (rate_limit, timeout, etc.) in generated configs. */\nconst FULL_PROVIDERS = ['pubmed', 'eric', 'arxiv', 'scopus'] as const;\n/** Provider names that only carry the enabled flag in generated configs. */\nconst MINIMAL_PROVIDERS = ['wos', 'embase'] as const;\n\n/**\n * Build the providers TOML object from a Config, excluding secrets.\n */\nfunction buildProvidersToml(config: Config): Record<string, Partial<ProviderConfig>> {\n const providers: Record<string, Partial<ProviderConfig>> = {};\n for (const name of FULL_PROVIDERS) {\n const p = config.providers[name];\n providers[name] = {\n enabled: p.enabled,\n rate_limit: p.rate_limit,\n timeout: p.timeout,\n retries: p.retries,\n max_results: p.max_results,\n };\n }\n for (const name of MINIMAL_PROVIDERS) {\n providers[name] = { enabled: config.providers[name].enabled };\n }\n return providers;\n}\n\n/**\n * Options for the init command.\n */\nexport interface InitOptions {\n /** Directory to create .search-hub/ in (defaults to cwd) */\n directory?: string;\n /** Initialize global config instead of local project */\n global?: boolean;\n /** Config directory for global init (defaults to platform-specific via getConfigDir()) */\n configDir?: string;\n /** Force overwrite if directory already exists */\n force?: boolean;\n}\n\n/**\n * Result of the init command.\n */\nexport interface InitResult {\n /** Whether initialization was successful */\n success: boolean;\n /** Path to the created config file */\n configPath: string;\n /** Path to the .search-hub/ project directory (local init only) */\n projectDir?: string;\n /** Whether files already existed (only when success=false) */\n alreadyExists?: boolean;\n /** Whether existing files were overwritten (only when force=true) */\n overwritten?: boolean;\n /** Message describing the result */\n message?: string;\n /** Actionable hints for the user */\n hints?: string[];\n}\n\n/**\n * Check if a file or directory exists.\n */\nasync function exists(path: string): Promise<boolean> {\n try {\n await access(path, constants.F_OK);\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Convert Config to TOML-compatible object for local project config.\n * Excludes secrets (api_key, email, inst_token).\n */\nfunction localConfigToToml(config: Config): Record<string, unknown> {\n return {\n providers: buildProvidersToml(config),\n integration: {\n reference_manager: {\n enabled: config.integration.reference_manager.enabled,\n command: config.integration.reference_manager.command,\n auto_register: config.integration.reference_manager.auto_register,\n },\n },\n };\n}\n\n/**\n * Generate TOML config content for global config with credential hints as comments.\n */\nfunction generateGlobalConfigContent(config: Config): string {\n const tomlObj = {\n log: {\n level: config.log.level,\n },\n output: {\n color: config.output.color,\n progress_bar: config.output.progress_bar,\n },\n providers: buildProvidersToml(config),\n integration: {\n reference_manager: {\n enabled: config.integration.reference_manager.enabled,\n command: config.integration.reference_manager.command,\n auto_register: config.integration.reference_manager.auto_register,\n },\n },\n };\n\n const header = `# search-hub global configuration\n# See: https://github.com/search-hub/search-hub for documentation\n#\n# [session]\n# directory = \"\" # Override default session directory (empty = platform default)\n\n`;\n\n const tomlContent = stringifyToml(tomlObj as Parameters<typeof stringifyToml>[0]);\n\n // Add credential hints as comments after provider sections\n const lines = tomlContent.split('\\n');\n const result: string[] = [];\n for (const line of lines) {\n result.push(line);\n if (line.startsWith('[providers.pubmed]')) {\n result.push('# api_key = \"\" # Optional but recommended (NCBI E-utilities)');\n result.push('# email = \"\" # Required by NCBI for tracking');\n } else if (line.startsWith('[providers.scopus]')) {\n result.push('# api_key = \"\" # Required for Scopus access');\n result.push('# inst_token = \"\" # Optional institutional token');\n } else if (line.startsWith('[providers.wos]')) {\n result.push('# api_key = \"\" # Required for Web of Science');\n }\n }\n\n return header + result.join('\\n');\n}\n\n/**\n * Generate TOML config file content for local project config.\n */\nfunction generateLocalConfigContent(config: Config): string {\n const tomlObj = localConfigToToml(config);\n const header = `# search-hub project configuration\n# Project-specific overrides (no secrets - use env vars or global config for API keys)\n\n`;\n return header + stringifyToml(tomlObj as Parameters<typeof stringifyToml>[0]);\n}\n\n/**\n * Initialize a local .search-hub/ project directory.\n */\nasync function initLocal(directory: string, force: boolean): Promise<InitResult> {\n const projectDir = getProjectDir(directory);\n const configPath = join(projectDir, 'config.toml');\n const sessionsDir = join(projectDir, 'sessions');\n const queriesDir = join(projectDir, 'queries');\n\n const result: InitResult = {\n success: false,\n configPath,\n projectDir,\n };\n\n // Check if .search-hub/ already exists\n if (await exists(projectDir)) {\n if (!force) {\n return {\n ...result,\n alreadyExists: true,\n message: `Project directory already exists at ${projectDir}. Use --force to overwrite.`,\n };\n }\n result.overwritten = true;\n }\n\n // Create directories\n await mkdir(projectDir, { recursive: true });\n await mkdir(sessionsDir, { recursive: true });\n await mkdir(queriesDir, { recursive: true });\n\n // Generate and write local config (no secrets)\n const defaultConfig = getDefaultConfig();\n const configContent = generateLocalConfigContent(defaultConfig);\n await writeFile(configPath, configContent, 'utf-8');\n\n return {\n ...result,\n success: true,\n message: result.overwritten\n ? `Project re-initialized at ${projectDir}`\n : `Project initialized at ${projectDir}`,\n hints: [\n 'Set up global credentials: search-hub init --global',\n 'Or use environment variables: see search-hub config --env-vars',\n 'API keys can also be set via .env file in the project root',\n ],\n };\n}\n\n/**\n * Initialize the global search-hub configuration.\n */\nasync function initGlobal(configDir: string, force: boolean): Promise<InitResult> {\n const configPath = join(configDir, 'config.toml');\n\n const result: InitResult = {\n success: false,\n configPath,\n };\n\n // Check if config directory already exists\n if (await exists(configDir)) {\n if (!force) {\n return {\n ...result,\n alreadyExists: true,\n message: `Global configuration already exists at ${configDir}. Use --force to overwrite.`,\n };\n }\n result.overwritten = true;\n }\n\n // Create directory\n await mkdir(configDir, { recursive: true });\n\n // Generate and write global config with credential hints\n const defaultConfig = getDefaultConfig();\n const configContent = generateGlobalConfigContent(defaultConfig);\n await writeFile(configPath, configContent, 'utf-8');\n\n return {\n ...result,\n success: true,\n message: result.overwritten\n ? `Global configuration overwritten at ${configDir}`\n : `Global configuration created at ${configDir}`,\n hints: [\n `Edit credentials: search-hub config --global set providers.pubmed.api_key <key>`,\n `Or edit directly: ${configPath}`,\n ],\n };\n}\n\n/**\n * Initialize search-hub configuration.\n *\n * By default, creates a `.search-hub/` project directory in the specified directory (or cwd).\n * With `--global`, creates the global config at the XDG-compliant path.\n */\nexport async function init(options: InitOptions = {}): Promise<InitResult> {\n const { force = false } = options;\n\n if (options.global) {\n const configDir = options.configDir ?? getConfigDir();\n return initGlobal(configDir, force);\n }\n\n const directory = options.directory ?? process.cwd();\n return initLocal(directory, force);\n}\n"],"names":["stringifyToml"],"mappings":"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"}
@@ -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;"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":";AAQA,OAAO,EAAE,OAAO,EAAU,MAAM,WAAW,CAAC;AAyM5C;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,0BAA0B;IAC1B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,gCAAgC;IAChC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,4BAA4B;IAC5B,OAAO,EAAE,OAAO,CAAC;IACjB,wCAAwC;IACxC,KAAK,EAAE,OAAO,CAAC;IACf,qEAAqE;IACrE,KAAK,EAAE,OAAO,CAAC;CAChB;AAED;;GAEG;AACH,wBAAgB,aAAa,IAAI,OAAO,CA+vFvC;AAED;;GAEG;AACH,wBAAsB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAG1C"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":";AAQA,OAAO,EAAE,OAAO,EAAU,MAAM,WAAW,CAAC;AAoN5C;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,0BAA0B;IAC1B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,gCAAgC;IAChC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,4BAA4B;IAC5B,OAAO,EAAE,OAAO,CAAC;IACjB,wCAAwC;IACxC,KAAK,EAAE,OAAO,CAAC;IACf,qEAAqE;IACrE,KAAK,EAAE,OAAO,CAAC;CAChB;AAED;;GAEG;AACH,wBAAgB,aAAa,IAAI,OAAO,CAg8FvC;AAED;;GAEG;AACH,wBAAsB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAG1C"}