fss-link 1.2.12 → 1.2.15

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 (2) hide show
  1. package/bundle/fss-link.js +290 -37
  2. package/package.json +1 -1
@@ -19810,16 +19810,19 @@ var init_openaiContentGenerator = __esm({
19810
19810
  } : isDashScopeProvider ? {
19811
19811
  "X-DashScope-CacheControl": "enable",
19812
19812
  "X-DashScope-UserAgent": userAgent,
19813
- "X-DashScope-AuthType": contentGeneratorConfig.authType
19813
+ "X-DashScope-AuthType": String(contentGeneratorConfig.authType ?? "")
19814
19814
  } : {}
19815
19815
  };
19816
- this.client = new OpenAI({
19816
+ const clientConfig = {
19817
19817
  apiKey: contentGeneratorConfig.apiKey,
19818
- baseURL: contentGeneratorConfig.baseUrl,
19819
19818
  timeout: contentGeneratorConfig.timeout ?? 12e4,
19820
19819
  maxRetries: contentGeneratorConfig.maxRetries ?? 3,
19821
19820
  defaultHeaders
19822
- });
19821
+ };
19822
+ if (contentGeneratorConfig.baseUrl && contentGeneratorConfig.baseUrl.trim() !== "") {
19823
+ clientConfig["baseURL"] = contentGeneratorConfig.baseUrl;
19824
+ }
19825
+ this.client = new OpenAI(clientConfig);
19823
19826
  }
19824
19827
  /**
19825
19828
  * Hook for subclasses to customize error handling behavior
@@ -22268,7 +22271,9 @@ async function createContentGeneratorConfig(config, authType) {
22268
22271
  }
22269
22272
  if (authType === AuthType.USE_OPENAI && openaiApiKey) {
22270
22273
  contentGeneratorConfig.apiKey = openaiApiKey;
22271
- contentGeneratorConfig.baseUrl = openaiBaseUrl;
22274
+ if (openaiBaseUrl && openaiBaseUrl.trim() !== "") {
22275
+ contentGeneratorConfig.baseUrl = openaiBaseUrl;
22276
+ }
22272
22277
  contentGeneratorConfig.model = openaiModel || DEFAULT_QWEN_MODEL;
22273
22278
  return contentGeneratorConfig;
22274
22279
  }
@@ -22342,7 +22347,7 @@ async function createContentGeneratorConfig(config, authType) {
22342
22347
  async function createContentGenerator(config, gcConfig, sessionId2) {
22343
22348
  if (DEBUG_CONTENT)
22344
22349
  console.log(`\u{1F41B} DEBUG createContentGenerator: authType=${config.authType}, apiKey=${config.apiKey}, baseUrl=${config.baseUrl}`);
22345
- const version = "1.2.12";
22350
+ const version = "1.2.15";
22346
22351
  const userAgent = `FSS-Link/${version} (${process.platform}; ${process.arch})`;
22347
22352
  const baseHeaders = {
22348
22353
  "User-Agent": userAgent
@@ -60695,7 +60700,7 @@ var init_project_structure = __esm({
60695
60700
  init_text_analyzer();
60696
60701
  projectStructureToolSchemaData = {
60697
60702
  name: "project_structure",
60698
- description: "Analyze and visualize project structure with intelligent insights. Goes beyond basic tree to provide architecture analysis, file relationships, and development guidance.",
60703
+ description: "Query project files with focused filters and smart defaults. Use query parameters for targeted searches (large files, recent changes, specific types) or no parameters for concise overview.",
60699
60704
  parametersJsonSchema: {
60700
60705
  type: "object",
60701
60706
  properties: {
@@ -60704,9 +60709,74 @@ var init_project_structure = __esm({
60704
60709
  description: "Directory to analyze (defaults to current directory)",
60705
60710
  default: "."
60706
60711
  },
60712
+ query: {
60713
+ type: "object",
60714
+ description: "Focused query parameters for targeted file searches (takes precedence over analysis_type)",
60715
+ properties: {
60716
+ where: {
60717
+ type: "object",
60718
+ description: "Filter conditions",
60719
+ properties: {
60720
+ extension: {
60721
+ description: 'File extension(s) to match (e.g., "ts" or ["ts", "tsx"])'
60722
+ },
60723
+ size_gt: {
60724
+ type: "number",
60725
+ description: "Minimum file size in bytes"
60726
+ },
60727
+ size_lt: {
60728
+ type: "number",
60729
+ description: "Maximum file size in bytes"
60730
+ },
60731
+ path_contains: {
60732
+ type: "string",
60733
+ description: "Path must contain this string"
60734
+ },
60735
+ is_entry_point: {
60736
+ type: "boolean",
60737
+ description: "Filter to entry point files"
60738
+ },
60739
+ is_config: {
60740
+ type: "boolean",
60741
+ description: "Filter to configuration files"
60742
+ },
60743
+ modified_within_days: {
60744
+ type: "number",
60745
+ description: "Files modified within N days"
60746
+ },
60747
+ depth_lte: {
60748
+ type: "number",
60749
+ description: "Maximum directory depth"
60750
+ }
60751
+ }
60752
+ },
60753
+ group_by: {
60754
+ type: "string",
60755
+ description: "Group results by field",
60756
+ enum: ["extension", "directory"]
60757
+ },
60758
+ order_by: {
60759
+ type: "string",
60760
+ description: "Sort field",
60761
+ enum: ["path", "size", "modified"],
60762
+ default: "modified"
60763
+ },
60764
+ order_dir: {
60765
+ type: "string",
60766
+ description: "Sort direction",
60767
+ enum: ["asc", "desc"],
60768
+ default: "desc"
60769
+ },
60770
+ limit: {
60771
+ type: "number",
60772
+ description: "Maximum number of results",
60773
+ default: 20
60774
+ }
60775
+ }
60776
+ },
60707
60777
  analysis_type: {
60708
60778
  type: "string",
60709
- description: "Type of analysis to perform",
60779
+ description: "Legacy: Type of analysis to perform (use query for focused searches)",
60710
60780
  enum: ["tree", "overview", "architecture", "metrics", "dependencies", "content"],
60711
60781
  default: "overview"
60712
60782
  },
@@ -60746,31 +60816,36 @@ var init_project_structure = __esm({
60746
60816
  }
60747
60817
  };
60748
60818
  projectStructureToolDescription = `
60749
- Intelligent project structure analysis and visualization tool for development workflows.
60819
+ Query project files with focused filters and smart defaults.
60750
60820
 
60751
- Analysis Types:
60752
- - **tree**: Enhanced directory tree with file type icons and metadata
60753
- - **overview**: High-level project summary with entry points and key files
60754
- - **architecture**: Module relationships and dependency analysis
60755
- - **metrics**: Code metrics, file sizes, and complexity analysis
60756
- - **dependencies**: Package dependencies and version analysis
60757
- - **content**: Text analysis with readability scores, complexity ratings, and documentation quality
60821
+ **RECOMMENDED: Use query mode for targeted searches**
60758
60822
 
60759
- Features:
60760
- - Smart file type detection with appropriate icons
60761
- - Git integration (modified files, commit history)
60762
- - Large file and bloat detection
60763
- - Entry point and configuration file identification
60764
- - Module import/export analysis
60765
- - Architecture pattern recognition
60766
- - Technology stack detection
60823
+ Common Query Patterns:
60824
+ - Large files: { query: { where: { size_gt: 100000 }, order_by: 'size', limit: 10 } }
60825
+ - Recent changes: { query: { where: { modified_within_days: 7 }, order_by: 'modified' } }
60826
+ - TypeScript files: { query: { where: { extension: ['ts', 'tsx'] } } }
60827
+ - File type distribution: { query: { group_by: 'extension' } }
60828
+ - Config files: { query: { where: { is_config: true } } }
60829
+ - Entry points: { query: { where: { is_entry_point: true } } }
60767
60830
 
60768
- Perfect for:
60769
- - Understanding unfamiliar codebases
60770
- - Identifying architectural issues
60771
- - Finding entry points and key files
60772
- - Detecting bloat and unused files
60773
- - Planning refactoring efforts
60831
+ Query Parameters:
60832
+ - **where**: Filter conditions (extension, size_gt/lt, path_contains, is_entry_point, is_config, modified_within_days, depth_lte)
60833
+ - **group_by**: Group results (extension, directory)
60834
+ - **order_by**: Sort field (path, size, modified)
60835
+ - **limit**: Max results (default: 20)
60836
+
60837
+ Smart Default (no parameters):
60838
+ Returns concise project overview with entry points, recent changes, and largest files (~20 lines).
60839
+
60840
+ Legacy Analysis Types (deprecated - use query instead):
60841
+ - overview: Project summary
60842
+ - content: Text analysis (expensive, opt-in only)
60843
+
60844
+ Features:
60845
+ - Focused queries (10-20 line results vs 50-100 line dumps)
60846
+ - Smart filtering and aggregation
60847
+ - File type detection with icons
60848
+ - Size and modification time analysis
60774
60849
  `;
60775
60850
  ProjectStructureToolInvocation = class extends BaseToolInvocation {
60776
60851
  constructor(params) {
@@ -60781,7 +60856,7 @@ Perfect for:
60781
60856
  return `Analyze project structure (${analysis_type}) for ${target}`;
60782
60857
  }
60783
60858
  async execute(_signal) {
60784
- const { target = ".", analysis_type = "overview", max_depth = 5, show_hidden = false, file_types, exclude_patterns = ["node_modules", ".git", "__pycache__", "dist", "build"], size_analysis = true, git_awareness = true } = this.params;
60859
+ const { target = ".", analysis_type = "overview", max_depth = 5, show_hidden = false, file_types, exclude_patterns = ["node_modules", ".git", "__pycache__", "dist", "build"], size_analysis = true, git_awareness = true, query } = this.params;
60785
60860
  try {
60786
60861
  const targetPath = path27.resolve(target);
60787
60862
  try {
@@ -60804,6 +60879,20 @@ Perfect for:
60804
60879
  returnDisplay: `\u274C Directory does not exist: ${target}`
60805
60880
  };
60806
60881
  }
60882
+ if (query) {
60883
+ const results = await this.executeQuery(targetPath, query, exclude_patterns);
60884
+ const display2 = this.formatQueryResults(results, query);
60885
+ return {
60886
+ llmContent: JSON.stringify({
60887
+ success: true,
60888
+ mode: "query",
60889
+ target: targetPath,
60890
+ query,
60891
+ results: results.summary || { files: results.files.map((f) => ({ path: f.path, size: f.size })) }
60892
+ }),
60893
+ returnDisplay: display2
60894
+ };
60895
+ }
60807
60896
  let analysis;
60808
60897
  switch (analysis_type) {
60809
60898
  case "tree":
@@ -61474,6 +61563,148 @@ Perfect for:
61474
61563
  }
61475
61564
  return `${size.toFixed(1)} ${units[unitIndex]}`;
61476
61565
  }
61566
+ // ========== QUERY MODE IMPLEMENTATION ==========
61567
+ /**
61568
+ * Execute a focused query on project files
61569
+ */
61570
+ async executeQuery(targetPath, query, excludePatterns) {
61571
+ const maxDepth = query.where?.depth_lte ?? 10;
61572
+ const structure = await this.scanDirectory(targetPath, "", 0, maxDepth, false, excludePatterns);
61573
+ let filtered = this.applyQueryFilters(structure, query.where);
61574
+ if (query.order_by) {
61575
+ filtered = this.applyQueryOrdering(filtered, query.order_by, query.order_dir ?? "desc");
61576
+ }
61577
+ const limit2 = query.limit ?? 20;
61578
+ filtered = filtered.slice(0, limit2);
61579
+ if (query.group_by) {
61580
+ return this.applyQueryGrouping(filtered, query.group_by);
61581
+ }
61582
+ return { files: filtered };
61583
+ }
61584
+ /**
61585
+ * Apply query filters to file list
61586
+ */
61587
+ applyQueryFilters(files, where) {
61588
+ if (!where)
61589
+ return files;
61590
+ return files.filter((file) => {
61591
+ if (where.extension) {
61592
+ const extensions = Array.isArray(where.extension) ? where.extension : [where.extension];
61593
+ if (file.extension && !extensions.includes(file.extension))
61594
+ return false;
61595
+ }
61596
+ if (where.size_gt !== void 0 && file.size <= where.size_gt)
61597
+ return false;
61598
+ if (where.size_lt !== void 0 && file.size >= where.size_lt)
61599
+ return false;
61600
+ if (where.path_contains && !file.path.includes(where.path_contains))
61601
+ return false;
61602
+ if (where.is_entry_point !== void 0 && file.isEntryPoint !== where.is_entry_point)
61603
+ return false;
61604
+ if (where.is_config !== void 0 && file.isConfig !== where.is_config)
61605
+ return false;
61606
+ if (where.modified_within_days !== void 0) {
61607
+ const daysSinceModified = (Date.now() - file.lastModified.getTime()) / (1e3 * 60 * 60 * 24);
61608
+ if (daysSinceModified > where.modified_within_days)
61609
+ return false;
61610
+ }
61611
+ if (where.depth_lte !== void 0) {
61612
+ const depth = file.path.split("/").length;
61613
+ if (depth > where.depth_lte)
61614
+ return false;
61615
+ }
61616
+ return true;
61617
+ });
61618
+ }
61619
+ /**
61620
+ * Apply ordering to filtered files
61621
+ */
61622
+ applyQueryOrdering(files, orderBy, orderDir) {
61623
+ const sorted = [...files];
61624
+ const multiplier = orderDir === "asc" ? 1 : -1;
61625
+ sorted.sort((a, b) => {
61626
+ switch (orderBy) {
61627
+ case "size":
61628
+ return (b.size - a.size) * multiplier;
61629
+ case "modified":
61630
+ return (b.lastModified.getTime() - a.lastModified.getTime()) * multiplier;
61631
+ case "path":
61632
+ default:
61633
+ return a.path.localeCompare(b.path) * multiplier;
61634
+ }
61635
+ });
61636
+ return sorted;
61637
+ }
61638
+ /**
61639
+ * Apply grouping and aggregation
61640
+ */
61641
+ applyQueryGrouping(files, groupBy) {
61642
+ const groups = {};
61643
+ files.forEach((file) => {
61644
+ let key;
61645
+ switch (groupBy) {
61646
+ case "extension":
61647
+ key = file.extension || "no-extension";
61648
+ break;
61649
+ case "directory":
61650
+ key = file.path.split("/")[0] || "root";
61651
+ break;
61652
+ default:
61653
+ key = "unknown";
61654
+ }
61655
+ if (!groups[key])
61656
+ groups[key] = [];
61657
+ groups[key].push(file);
61658
+ });
61659
+ const summary = {};
61660
+ Object.keys(groups).forEach((key) => {
61661
+ const groupFiles2 = groups[key];
61662
+ summary[key] = {
61663
+ count: groupFiles2.length,
61664
+ total_size: groupFiles2.reduce((sum, f) => sum + f.size, 0),
61665
+ files: groupFiles2.slice(0, 5).map((f) => f.path)
61666
+ };
61667
+ });
61668
+ return { files, summary };
61669
+ }
61670
+ /**
61671
+ * Format query results for display
61672
+ */
61673
+ formatQueryResults(results, query) {
61674
+ let output = "";
61675
+ if (results.summary) {
61676
+ output += `**Query Results (grouped by ${query.group_by}):**
61677
+
61678
+ `;
61679
+ Object.entries(results.summary).forEach(([key, value]) => {
61680
+ output += `**${key}:** ${value.count} files, ${this.formatSize(value.total_size)}
61681
+ `;
61682
+ if (value.files && value.files.length > 0) {
61683
+ value.files.forEach((filePath) => {
61684
+ output += ` \u2022 ${filePath}
61685
+ `;
61686
+ });
61687
+ }
61688
+ output += "\n";
61689
+ });
61690
+ } else {
61691
+ output += `**Query Results:** ${results.files.length} files
61692
+
61693
+ `;
61694
+ results.files.forEach((file) => {
61695
+ output += `${file.icon} ${file.path}`;
61696
+ if (query.select?.includes("size") || !query.select) {
61697
+ output += ` (${this.formatSize(file.size)})`;
61698
+ }
61699
+ if (query.select?.includes("modified") || !query.select) {
61700
+ const daysAgo = Math.floor((Date.now() - file.lastModified.getTime()) / (1e3 * 60 * 60 * 24));
61701
+ output += ` - ${daysAgo}d ago`;
61702
+ }
61703
+ output += "\n";
61704
+ });
61705
+ }
61706
+ return output;
61707
+ }
61477
61708
  };
61478
61709
  ProjectStructureTool = class _ProjectStructureTool extends BaseDeclarativeTool {
61479
61710
  static Name = projectStructureToolSchemaData.name;
@@ -82286,6 +82517,12 @@ function loadSettings(workspaceDir) {
82286
82517
  }
82287
82518
  function saveSettings(settingsFile) {
82288
82519
  try {
82520
+ console.log("[SETTINGS DEBUG] saveSettings called:", {
82521
+ path: settingsFile.path,
82522
+ dirname: path56.dirname(settingsFile.path),
82523
+ keys: Object.keys(settingsFile.settings),
82524
+ stackTrace: new Error().stack?.split("\n").slice(2, 6).join("\n")
82525
+ });
82289
82526
  const dirPath = path56.dirname(settingsFile.path);
82290
82527
  if (!fs49.existsSync(dirPath)) {
82291
82528
  fs49.mkdirSync(dirPath, { recursive: true });
@@ -86199,7 +86436,11 @@ var init_modelManager = __esm({
86199
86436
  case AuthType.USE_OPENAI:
86200
86437
  case "openai":
86201
86438
  process.env["OPENAI_API_KEY"] = model.apiKey || "";
86202
- process.env["OPENAI_BASE_URL"] = model.endpointUrl || "";
86439
+ if (model.endpointUrl && model.endpointUrl.trim() !== "") {
86440
+ process.env["OPENAI_BASE_URL"] = model.endpointUrl;
86441
+ } else {
86442
+ delete process.env["OPENAI_BASE_URL"];
86443
+ }
86203
86444
  process.env["OPENAI_MODEL"] = model.modelName || "";
86204
86445
  break;
86205
86446
  case AuthType.OLLAMA:
@@ -95173,7 +95414,7 @@ async function getPackageJson() {
95173
95414
  // packages/cli/src/utils/version.ts
95174
95415
  async function getCliVersion() {
95175
95416
  const pkgJson = await getPackageJson();
95176
- return "1.2.12";
95417
+ return "1.2.15";
95177
95418
  }
95178
95419
 
95179
95420
  // packages/cli/src/ui/commands/aboutCommand.ts
@@ -95225,7 +95466,7 @@ import open4 from "open";
95225
95466
  import process11 from "node:process";
95226
95467
 
95227
95468
  // packages/cli/src/generated/git-commit.ts
95228
- var GIT_COMMIT_INFO = "69072e25";
95469
+ var GIT_COMMIT_INFO = "60f96849";
95229
95470
 
95230
95471
  // packages/cli/src/ui/commands/bugCommand.ts
95231
95472
  init_dist2();
@@ -121467,17 +121708,29 @@ async function loadCliConfig(settings, extensions, sessionId2, argv, cwd3 = proc
121467
121708
  try {
121468
121709
  const { SearchEngineConfigProvider: SearchEngineConfigProvider2 } = await Promise.resolve().then(() => (init_SearchEngineConfigProvider(), SearchEngineConfigProvider_exports));
121469
121710
  const configProvider = SearchEngineConfigProvider2.getInstance();
121470
- await configProvider.loadConfiguration();
121711
+ const config2 = await configProvider.loadConfiguration();
121712
+ console.log("[API KEY DEBUG] Search engine config loaded at startup:", {
121713
+ hasBrave: !!config2.braveApiKey,
121714
+ hasTavily: !!config2.tavilyApiKey,
121715
+ braveEnvSet: !!process16.env["BRAVE_SEARCH_API_KEY"],
121716
+ tavilyEnvSet: !!process16.env["TAVILY_API_KEY"]
121717
+ });
121471
121718
  } catch (error) {
121719
+ console.error("[API KEY DEBUG] Failed to load search engine config at startup:", error);
121472
121720
  }
121473
121721
  const webScraperConfigLoader = () => {
121474
121722
  try {
121475
- return {
121723
+ const config2 = {
121476
121724
  braveApiKey: process16.env["BRAVE_SEARCH_API_KEY"],
121477
121725
  tavilyApiKey: process16.env["TAVILY_API_KEY"]
121478
121726
  };
121727
+ console.log("[API KEY DEBUG] webScraperConfigLoader called:", {
121728
+ hasBrave: !!config2.braveApiKey,
121729
+ hasTavily: !!config2.tavilyApiKey
121730
+ });
121731
+ return config2;
121479
121732
  } catch (error) {
121480
- console.warn("Failed to load search engine configuration:", error);
121733
+ console.warn("[API KEY DEBUG] Failed to load search engine configuration:", error);
121481
121734
  return null;
121482
121735
  }
121483
121736
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fss-link",
3
- "version": "1.2.12",
3
+ "version": "1.2.15",
4
4
  "engines": {
5
5
  "node": ">=20.0.0"
6
6
  },