fss-link 1.2.13 → 1.2.16

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 +283 -40
  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
@@ -19869,11 +19872,7 @@ var init_openaiContentGenerator = __esm({
19869
19872
  if (this.contentGeneratorConfig.authType === "ollama") {
19870
19873
  return true;
19871
19874
  }
19872
- const baseUrl = this.contentGeneratorConfig.baseUrl;
19873
- if (!baseUrl)
19874
- return false;
19875
- return baseUrl.includes("localhost:11434") || baseUrl.includes("127.0.0.1:11434") || baseUrl.includes(":11434/v1") || baseUrl.includes(":11435/v1") || // Support proxy port
19876
- baseUrl.includes("/ollama");
19875
+ return false;
19877
19876
  }
19878
19877
  /**
19879
19878
  * Determine if this is a custom OpenAI-compatible endpoint (proxy, local server, etc.)
@@ -22268,7 +22267,9 @@ async function createContentGeneratorConfig(config, authType) {
22268
22267
  }
22269
22268
  if (authType === AuthType.USE_OPENAI && openaiApiKey) {
22270
22269
  contentGeneratorConfig.apiKey = openaiApiKey;
22271
- contentGeneratorConfig.baseUrl = openaiBaseUrl;
22270
+ if (openaiBaseUrl && openaiBaseUrl.trim() !== "") {
22271
+ contentGeneratorConfig.baseUrl = openaiBaseUrl;
22272
+ }
22272
22273
  contentGeneratorConfig.model = openaiModel || DEFAULT_QWEN_MODEL;
22273
22274
  return contentGeneratorConfig;
22274
22275
  }
@@ -22342,7 +22343,7 @@ async function createContentGeneratorConfig(config, authType) {
22342
22343
  async function createContentGenerator(config, gcConfig, sessionId2) {
22343
22344
  if (DEBUG_CONTENT)
22344
22345
  console.log(`\u{1F41B} DEBUG createContentGenerator: authType=${config.authType}, apiKey=${config.apiKey}, baseUrl=${config.baseUrl}`);
22345
- const version = "1.2.13";
22346
+ const version = "1.2.16";
22346
22347
  const userAgent = `FSS-Link/${version} (${process.platform}; ${process.arch})`;
22347
22348
  const baseHeaders = {
22348
22349
  "User-Agent": userAgent
@@ -60695,7 +60696,7 @@ var init_project_structure = __esm({
60695
60696
  init_text_analyzer();
60696
60697
  projectStructureToolSchemaData = {
60697
60698
  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.",
60699
+ 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
60700
  parametersJsonSchema: {
60700
60701
  type: "object",
60701
60702
  properties: {
@@ -60704,9 +60705,74 @@ var init_project_structure = __esm({
60704
60705
  description: "Directory to analyze (defaults to current directory)",
60705
60706
  default: "."
60706
60707
  },
60708
+ query: {
60709
+ type: "object",
60710
+ description: "Focused query parameters for targeted file searches (takes precedence over analysis_type)",
60711
+ properties: {
60712
+ where: {
60713
+ type: "object",
60714
+ description: "Filter conditions",
60715
+ properties: {
60716
+ extension: {
60717
+ description: 'File extension(s) to match (e.g., "ts" or ["ts", "tsx"])'
60718
+ },
60719
+ size_gt: {
60720
+ type: "number",
60721
+ description: "Minimum file size in bytes"
60722
+ },
60723
+ size_lt: {
60724
+ type: "number",
60725
+ description: "Maximum file size in bytes"
60726
+ },
60727
+ path_contains: {
60728
+ type: "string",
60729
+ description: "Path must contain this string"
60730
+ },
60731
+ is_entry_point: {
60732
+ type: "boolean",
60733
+ description: "Filter to entry point files"
60734
+ },
60735
+ is_config: {
60736
+ type: "boolean",
60737
+ description: "Filter to configuration files"
60738
+ },
60739
+ modified_within_days: {
60740
+ type: "number",
60741
+ description: "Files modified within N days"
60742
+ },
60743
+ depth_lte: {
60744
+ type: "number",
60745
+ description: "Maximum directory depth"
60746
+ }
60747
+ }
60748
+ },
60749
+ group_by: {
60750
+ type: "string",
60751
+ description: "Group results by field",
60752
+ enum: ["extension", "directory"]
60753
+ },
60754
+ order_by: {
60755
+ type: "string",
60756
+ description: "Sort field",
60757
+ enum: ["path", "size", "modified"],
60758
+ default: "modified"
60759
+ },
60760
+ order_dir: {
60761
+ type: "string",
60762
+ description: "Sort direction",
60763
+ enum: ["asc", "desc"],
60764
+ default: "desc"
60765
+ },
60766
+ limit: {
60767
+ type: "number",
60768
+ description: "Maximum number of results",
60769
+ default: 20
60770
+ }
60771
+ }
60772
+ },
60707
60773
  analysis_type: {
60708
60774
  type: "string",
60709
- description: "Type of analysis to perform",
60775
+ description: "Legacy: Type of analysis to perform (use query for focused searches)",
60710
60776
  enum: ["tree", "overview", "architecture", "metrics", "dependencies", "content"],
60711
60777
  default: "overview"
60712
60778
  },
@@ -60746,31 +60812,36 @@ var init_project_structure = __esm({
60746
60812
  }
60747
60813
  };
60748
60814
  projectStructureToolDescription = `
60749
- Intelligent project structure analysis and visualization tool for development workflows.
60815
+ Query project files with focused filters and smart defaults.
60750
60816
 
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
60817
+ **RECOMMENDED: Use query mode for targeted searches**
60758
60818
 
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
60819
+ Common Query Patterns:
60820
+ - Large files: { query: { where: { size_gt: 100000 }, order_by: 'size', limit: 10 } }
60821
+ - Recent changes: { query: { where: { modified_within_days: 7 }, order_by: 'modified' } }
60822
+ - TypeScript files: { query: { where: { extension: ['ts', 'tsx'] } } }
60823
+ - File type distribution: { query: { group_by: 'extension' } }
60824
+ - Config files: { query: { where: { is_config: true } } }
60825
+ - Entry points: { query: { where: { is_entry_point: true } } }
60767
60826
 
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
60827
+ Query Parameters:
60828
+ - **where**: Filter conditions (extension, size_gt/lt, path_contains, is_entry_point, is_config, modified_within_days, depth_lte)
60829
+ - **group_by**: Group results (extension, directory)
60830
+ - **order_by**: Sort field (path, size, modified)
60831
+ - **limit**: Max results (default: 20)
60832
+
60833
+ Smart Default (no parameters):
60834
+ Returns concise project overview with entry points, recent changes, and largest files (~20 lines).
60835
+
60836
+ Legacy Analysis Types (deprecated - use query instead):
60837
+ - overview: Project summary
60838
+ - content: Text analysis (expensive, opt-in only)
60839
+
60840
+ Features:
60841
+ - Focused queries (10-20 line results vs 50-100 line dumps)
60842
+ - Smart filtering and aggregation
60843
+ - File type detection with icons
60844
+ - Size and modification time analysis
60774
60845
  `;
60775
60846
  ProjectStructureToolInvocation = class extends BaseToolInvocation {
60776
60847
  constructor(params) {
@@ -60781,7 +60852,7 @@ Perfect for:
60781
60852
  return `Analyze project structure (${analysis_type}) for ${target}`;
60782
60853
  }
60783
60854
  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;
60855
+ 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
60856
  try {
60786
60857
  const targetPath = path27.resolve(target);
60787
60858
  try {
@@ -60804,6 +60875,20 @@ Perfect for:
60804
60875
  returnDisplay: `\u274C Directory does not exist: ${target}`
60805
60876
  };
60806
60877
  }
60878
+ if (query) {
60879
+ const results = await this.executeQuery(targetPath, query, exclude_patterns);
60880
+ const display2 = this.formatQueryResults(results, query);
60881
+ return {
60882
+ llmContent: JSON.stringify({
60883
+ success: true,
60884
+ mode: "query",
60885
+ target: targetPath,
60886
+ query,
60887
+ results: results.summary || { files: results.files.map((f) => ({ path: f.path, size: f.size })) }
60888
+ }),
60889
+ returnDisplay: display2
60890
+ };
60891
+ }
60807
60892
  let analysis;
60808
60893
  switch (analysis_type) {
60809
60894
  case "tree":
@@ -61474,6 +61559,148 @@ Perfect for:
61474
61559
  }
61475
61560
  return `${size.toFixed(1)} ${units[unitIndex]}`;
61476
61561
  }
61562
+ // ========== QUERY MODE IMPLEMENTATION ==========
61563
+ /**
61564
+ * Execute a focused query on project files
61565
+ */
61566
+ async executeQuery(targetPath, query, excludePatterns) {
61567
+ const maxDepth = query.where?.depth_lte ?? 10;
61568
+ const structure = await this.scanDirectory(targetPath, "", 0, maxDepth, false, excludePatterns);
61569
+ let filtered = this.applyQueryFilters(structure, query.where);
61570
+ if (query.order_by) {
61571
+ filtered = this.applyQueryOrdering(filtered, query.order_by, query.order_dir ?? "desc");
61572
+ }
61573
+ const limit2 = query.limit ?? 20;
61574
+ filtered = filtered.slice(0, limit2);
61575
+ if (query.group_by) {
61576
+ return this.applyQueryGrouping(filtered, query.group_by);
61577
+ }
61578
+ return { files: filtered };
61579
+ }
61580
+ /**
61581
+ * Apply query filters to file list
61582
+ */
61583
+ applyQueryFilters(files, where) {
61584
+ if (!where)
61585
+ return files;
61586
+ return files.filter((file) => {
61587
+ if (where.extension) {
61588
+ const extensions = Array.isArray(where.extension) ? where.extension : [where.extension];
61589
+ if (file.extension && !extensions.includes(file.extension))
61590
+ return false;
61591
+ }
61592
+ if (where.size_gt !== void 0 && file.size <= where.size_gt)
61593
+ return false;
61594
+ if (where.size_lt !== void 0 && file.size >= where.size_lt)
61595
+ return false;
61596
+ if (where.path_contains && !file.path.includes(where.path_contains))
61597
+ return false;
61598
+ if (where.is_entry_point !== void 0 && file.isEntryPoint !== where.is_entry_point)
61599
+ return false;
61600
+ if (where.is_config !== void 0 && file.isConfig !== where.is_config)
61601
+ return false;
61602
+ if (where.modified_within_days !== void 0) {
61603
+ const daysSinceModified = (Date.now() - file.lastModified.getTime()) / (1e3 * 60 * 60 * 24);
61604
+ if (daysSinceModified > where.modified_within_days)
61605
+ return false;
61606
+ }
61607
+ if (where.depth_lte !== void 0) {
61608
+ const depth = file.path.split("/").length;
61609
+ if (depth > where.depth_lte)
61610
+ return false;
61611
+ }
61612
+ return true;
61613
+ });
61614
+ }
61615
+ /**
61616
+ * Apply ordering to filtered files
61617
+ */
61618
+ applyQueryOrdering(files, orderBy, orderDir) {
61619
+ const sorted = [...files];
61620
+ const multiplier = orderDir === "asc" ? 1 : -1;
61621
+ sorted.sort((a, b) => {
61622
+ switch (orderBy) {
61623
+ case "size":
61624
+ return (b.size - a.size) * multiplier;
61625
+ case "modified":
61626
+ return (b.lastModified.getTime() - a.lastModified.getTime()) * multiplier;
61627
+ case "path":
61628
+ default:
61629
+ return a.path.localeCompare(b.path) * multiplier;
61630
+ }
61631
+ });
61632
+ return sorted;
61633
+ }
61634
+ /**
61635
+ * Apply grouping and aggregation
61636
+ */
61637
+ applyQueryGrouping(files, groupBy) {
61638
+ const groups = {};
61639
+ files.forEach((file) => {
61640
+ let key;
61641
+ switch (groupBy) {
61642
+ case "extension":
61643
+ key = file.extension || "no-extension";
61644
+ break;
61645
+ case "directory":
61646
+ key = file.path.split("/")[0] || "root";
61647
+ break;
61648
+ default:
61649
+ key = "unknown";
61650
+ }
61651
+ if (!groups[key])
61652
+ groups[key] = [];
61653
+ groups[key].push(file);
61654
+ });
61655
+ const summary = {};
61656
+ Object.keys(groups).forEach((key) => {
61657
+ const groupFiles2 = groups[key];
61658
+ summary[key] = {
61659
+ count: groupFiles2.length,
61660
+ total_size: groupFiles2.reduce((sum, f) => sum + f.size, 0),
61661
+ files: groupFiles2.slice(0, 5).map((f) => f.path)
61662
+ };
61663
+ });
61664
+ return { files, summary };
61665
+ }
61666
+ /**
61667
+ * Format query results for display
61668
+ */
61669
+ formatQueryResults(results, query) {
61670
+ let output = "";
61671
+ if (results.summary) {
61672
+ output += `**Query Results (grouped by ${query.group_by}):**
61673
+
61674
+ `;
61675
+ Object.entries(results.summary).forEach(([key, value]) => {
61676
+ output += `**${key}:** ${value.count} files, ${this.formatSize(value.total_size)}
61677
+ `;
61678
+ if (value.files && value.files.length > 0) {
61679
+ value.files.forEach((filePath) => {
61680
+ output += ` \u2022 ${filePath}
61681
+ `;
61682
+ });
61683
+ }
61684
+ output += "\n";
61685
+ });
61686
+ } else {
61687
+ output += `**Query Results:** ${results.files.length} files
61688
+
61689
+ `;
61690
+ results.files.forEach((file) => {
61691
+ output += `${file.icon} ${file.path}`;
61692
+ if (query.select?.includes("size") || !query.select) {
61693
+ output += ` (${this.formatSize(file.size)})`;
61694
+ }
61695
+ if (query.select?.includes("modified") || !query.select) {
61696
+ const daysAgo = Math.floor((Date.now() - file.lastModified.getTime()) / (1e3 * 60 * 60 * 24));
61697
+ output += ` - ${daysAgo}d ago`;
61698
+ }
61699
+ output += "\n";
61700
+ });
61701
+ }
61702
+ return output;
61703
+ }
61477
61704
  };
61478
61705
  ProjectStructureTool = class _ProjectStructureTool extends BaseDeclarativeTool {
61479
61706
  static Name = projectStructureToolSchemaData.name;
@@ -68754,7 +68981,7 @@ var init_turn = __esm({
68754
68981
  /*curated*/
68755
68982
  true
68756
68983
  ), req];
68757
- await reportError(error, "Error when talking to Gemini API", contextForReport, "Turn.run-sendMessageStream");
68984
+ await reportError(error, "Error when talking to LLM API", contextForReport, "Turn.run-sendMessageStream");
68758
68985
  const status = typeof error === "object" && error !== null && "status" in error && typeof error.status === "number" ? error.status : void 0;
68759
68986
  const structuredError = {
68760
68987
  message: getErrorMessage(error),
@@ -82286,6 +82513,12 @@ function loadSettings(workspaceDir) {
82286
82513
  }
82287
82514
  function saveSettings(settingsFile) {
82288
82515
  try {
82516
+ console.log("[SETTINGS DEBUG] saveSettings called:", {
82517
+ path: settingsFile.path,
82518
+ dirname: path56.dirname(settingsFile.path),
82519
+ keys: Object.keys(settingsFile.settings),
82520
+ stackTrace: new Error().stack?.split("\n").slice(2, 6).join("\n")
82521
+ });
82289
82522
  const dirPath = path56.dirname(settingsFile.path);
82290
82523
  if (!fs49.existsSync(dirPath)) {
82291
82524
  fs49.mkdirSync(dirPath, { recursive: true });
@@ -86199,7 +86432,11 @@ var init_modelManager = __esm({
86199
86432
  case AuthType.USE_OPENAI:
86200
86433
  case "openai":
86201
86434
  process.env["OPENAI_API_KEY"] = model.apiKey || "";
86202
- process.env["OPENAI_BASE_URL"] = model.endpointUrl || "";
86435
+ if (model.endpointUrl && model.endpointUrl.trim() !== "") {
86436
+ process.env["OPENAI_BASE_URL"] = model.endpointUrl;
86437
+ } else {
86438
+ delete process.env["OPENAI_BASE_URL"];
86439
+ }
86203
86440
  process.env["OPENAI_MODEL"] = model.modelName || "";
86204
86441
  break;
86205
86442
  case AuthType.OLLAMA:
@@ -95173,7 +95410,7 @@ async function getPackageJson() {
95173
95410
  // packages/cli/src/utils/version.ts
95174
95411
  async function getCliVersion() {
95175
95412
  const pkgJson = await getPackageJson();
95176
- return "1.2.13";
95413
+ return "1.2.16";
95177
95414
  }
95178
95415
 
95179
95416
  // packages/cli/src/ui/commands/aboutCommand.ts
@@ -95225,7 +95462,7 @@ import open4 from "open";
95225
95462
  import process11 from "node:process";
95226
95463
 
95227
95464
  // packages/cli/src/generated/git-commit.ts
95228
- var GIT_COMMIT_INFO = "01a88da5";
95465
+ var GIT_COMMIT_INFO = "a766b474";
95229
95466
 
95230
95467
  // packages/cli/src/ui/commands/bugCommand.ts
95231
95468
  init_dist2();
@@ -127679,6 +127916,7 @@ async function runNonInteractive(config, input, prompt_id) {
127679
127916
  abortController.signal,
127680
127917
  prompt_id
127681
127918
  );
127919
+ let hasFinishedEvent = false;
127682
127920
  for await (const event of responseStream) {
127683
127921
  if (abortController.signal.aborted) {
127684
127922
  console.error("Operation cancelled.");
@@ -127694,6 +127932,8 @@ async function runNonInteractive(config, input, prompt_id) {
127694
127932
  id: toolCallRequest.callId
127695
127933
  };
127696
127934
  functionCalls.push(fc);
127935
+ } else if (event.type === GeminiEventType.Finished) {
127936
+ hasFinishedEvent = true;
127697
127937
  }
127698
127938
  }
127699
127939
  if (functionCalls.length > 0) {
@@ -127729,6 +127969,9 @@ async function runNonInteractive(config, input, prompt_id) {
127729
127969
  }
127730
127970
  }
127731
127971
  currentMessages = [{ role: "user", parts: toolResponseParts }];
127972
+ } else if (hasFinishedEvent) {
127973
+ process.stdout.write("\n");
127974
+ return;
127732
127975
  } else {
127733
127976
  process.stdout.write("\n");
127734
127977
  return;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fss-link",
3
- "version": "1.2.13",
3
+ "version": "1.2.16",
4
4
  "engines": {
5
5
  "node": ">=20.0.0"
6
6
  },