@nomad-e/bluma-cli 0.1.0 → 0.1.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 (2) hide show
  1. package/dist/main.js +120 -28
  2. package/package.json +1 -1
package/dist/main.js CHANGED
@@ -3071,8 +3071,9 @@ async function readArtifact(args) {
3071
3071
  import https from "https";
3072
3072
  import http from "http";
3073
3073
  var DEFAULT_SOURCES = ["reddit", "github", "stackoverflow"];
3074
- var MAX_RESULTS_DEFAULT = 10;
3075
- var REQUEST_TIMEOUT = 1e4;
3074
+ var MAX_RESULTS_DEFAULT = 5;
3075
+ var REQUEST_TIMEOUT = 15e3;
3076
+ var MAX_CONTENT_LENGTH = 4e3;
3076
3077
  function httpGet(url, customHeaders) {
3077
3078
  return new Promise((resolve, reject) => {
3078
3079
  const protocol = url.startsWith("https") ? https : http;
@@ -3081,7 +3082,6 @@ function httpGet(url, customHeaders) {
3081
3082
  "Accept": "application/json, text/html, */*",
3082
3083
  "Accept-Language": "en-US,en;q=0.9",
3083
3084
  "Accept-Encoding": "identity",
3084
- // Não usar gzip para simplificar
3085
3085
  "Cache-Control": "no-cache",
3086
3086
  "Connection": "keep-alive",
3087
3087
  ...customHeaders
@@ -3111,10 +3111,18 @@ function httpGet(url, customHeaders) {
3111
3111
  });
3112
3112
  });
3113
3113
  }
3114
+ function cleanContent(text, maxLength = MAX_CONTENT_LENGTH) {
3115
+ if (!text) return "";
3116
+ let cleaned = text.replace(/```[\s\S]*?```/g, (match) => match.substring(0, 500) + (match.length > 500 ? "\n[...code truncated...]" : "")).replace(/\n{3,}/g, "\n\n").trim();
3117
+ if (cleaned.length > maxLength) {
3118
+ cleaned = cleaned.substring(0, maxLength) + "\n\n[...content truncated...]";
3119
+ }
3120
+ return cleaned;
3121
+ }
3114
3122
  async function searchReddit(query, limit) {
3115
3123
  const results = [];
3116
3124
  try {
3117
- const subreddits = "programming+webdev+javascript+typescript+python+node+reactjs+learnprogramming";
3125
+ const subreddits = "programming+webdev+javascript+typescript+python+node+reactjs+learnprogramming+rust+golang+devops";
3118
3126
  const encodedQuery = encodeURIComponent(query);
3119
3127
  const url = `https://www.reddit.com/r/${subreddits}/search.json?q=${encodedQuery}&sort=relevance&limit=${limit}&restrict_sr=on`;
3120
3128
  const response = await httpGet(url);
@@ -3122,12 +3130,46 @@ async function searchReddit(query, limit) {
3122
3130
  if (data.data?.children) {
3123
3131
  for (const child of data.data.children.slice(0, limit)) {
3124
3132
  const post = child.data;
3133
+ let content = `# ${post.title}
3134
+
3135
+ `;
3136
+ content += `**Subreddit:** r/${post.subreddit} | **Score:** ${post.score} | **Comments:** ${post.num_comments}
3137
+
3138
+ `;
3139
+ if (post.selftext) {
3140
+ content += `## Post Content:
3141
+ ${cleanContent(post.selftext, 2e3)}
3142
+
3143
+ `;
3144
+ }
3145
+ try {
3146
+ const commentsUrl = `https://www.reddit.com${post.permalink}.json?limit=3&depth=1`;
3147
+ const commentsResponse = await httpGet(commentsUrl);
3148
+ const commentsData = JSON.parse(commentsResponse);
3149
+ if (commentsData[1]?.data?.children) {
3150
+ content += `## Top Comments:
3151
+ `;
3152
+ for (const comment of commentsData[1].data.children.slice(0, 3)) {
3153
+ if (comment.data?.body) {
3154
+ content += `
3155
+ **[${comment.data.score || 0} pts]** ${cleanContent(comment.data.body, 500)}
3156
+ `;
3157
+ }
3158
+ }
3159
+ }
3160
+ } catch {
3161
+ }
3125
3162
  results.push({
3126
3163
  title: post.title || "",
3127
3164
  url: `https://reddit.com${post.permalink}`,
3128
3165
  source: "reddit",
3129
- snippet: post.selftext?.substring(0, 200) || `r/${post.subreddit} - ${post.score} upvotes`,
3130
- score: post.score
3166
+ content: cleanContent(content),
3167
+ score: post.score,
3168
+ metadata: {
3169
+ author: post.author,
3170
+ date: new Date(post.created_utc * 1e3).toISOString().split("T")[0],
3171
+ comments_count: post.num_comments
3172
+ }
3131
3173
  });
3132
3174
  }
3133
3175
  }
@@ -3145,12 +3187,35 @@ async function searchGitHub(query, limit) {
3145
3187
  const data = JSON.parse(response);
3146
3188
  if (data.items) {
3147
3189
  for (const item of data.items.slice(0, limit)) {
3190
+ let content = `# ${item.title}
3191
+
3192
+ `;
3193
+ content += `**Repo:** ${item.repository_url?.split("/").slice(-2).join("/") || "unknown"}
3194
+ `;
3195
+ content += `**State:** ${item.state} | **Comments:** ${item.comments} | **Reactions:** ${item.reactions?.total_count || 0}
3196
+
3197
+ `;
3198
+ if (item.body) {
3199
+ content += `## Issue Description:
3200
+ ${cleanContent(item.body, 2500)}
3201
+ `;
3202
+ }
3203
+ if (item.labels?.length > 0) {
3204
+ content += `
3205
+ **Labels:** ${item.labels.map((l) => l.name).join(", ")}
3206
+ `;
3207
+ }
3148
3208
  results.push({
3149
3209
  title: item.title || "",
3150
3210
  url: item.html_url || "",
3151
3211
  source: "github",
3152
- snippet: item.body?.substring(0, 200) || `${item.comments} comments`,
3153
- score: item.reactions?.total_count || 0
3212
+ content: cleanContent(content),
3213
+ score: item.reactions?.total_count || 0,
3214
+ metadata: {
3215
+ author: item.user?.login,
3216
+ date: item.created_at?.split("T")[0],
3217
+ comments_count: item.comments
3218
+ }
3154
3219
  });
3155
3220
  }
3156
3221
  }
@@ -3163,17 +3228,56 @@ async function searchStackOverflow(query, limit) {
3163
3228
  const results = [];
3164
3229
  try {
3165
3230
  const encodedQuery = encodeURIComponent(query);
3166
- const url = `https://api.stackexchange.com/2.3/search?order=desc&sort=relevance&intitle=${encodedQuery}&site=stackoverflow&pagesize=${limit}`;
3231
+ const url = `https://api.stackexchange.com/2.3/search/advanced?order=desc&sort=relevance&q=${encodedQuery}&site=stackoverflow&pagesize=${limit}&filter=withbody`;
3167
3232
  const response = await httpGet(url);
3168
3233
  const data = JSON.parse(response);
3169
3234
  if (data.items) {
3170
3235
  for (const item of data.items.slice(0, limit)) {
3236
+ let content = `# ${item.title}
3237
+
3238
+ `;
3239
+ content += `**Score:** ${item.score} | **Answers:** ${item.answer_count} | **Views:** ${item.view_count}`;
3240
+ content += item.is_answered ? " | \u2705 Answered" : "";
3241
+ content += `
3242
+
3243
+ `;
3244
+ if (item.body) {
3245
+ const cleanBody = item.body.replace(/<code>/g, "`").replace(/<\/code>/g, "`").replace(/<pre>/g, "```\n").replace(/<\/pre>/g, "\n```").replace(/<[^>]+>/g, "").replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&amp;/g, "&").replace(/&quot;/g, '"');
3246
+ content += `## Question:
3247
+ ${cleanContent(cleanBody, 1500)}
3248
+
3249
+ `;
3250
+ }
3251
+ if (item.tags?.length > 0) {
3252
+ content += `**Tags:** ${item.tags.join(", ")}
3253
+
3254
+ `;
3255
+ }
3256
+ if (item.accepted_answer_id) {
3257
+ try {
3258
+ const answerUrl = `https://api.stackexchange.com/2.3/answers/${item.accepted_answer_id}?site=stackoverflow&filter=withbody`;
3259
+ const answerResponse = await httpGet(answerUrl);
3260
+ const answerData = JSON.parse(answerResponse);
3261
+ if (answerData.items?.[0]?.body) {
3262
+ const cleanAnswer = answerData.items[0].body.replace(/<code>/g, "`").replace(/<\/code>/g, "`").replace(/<pre>/g, "```\n").replace(/<\/pre>/g, "\n```").replace(/<[^>]+>/g, "").replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&amp;/g, "&").replace(/&quot;/g, '"');
3263
+ content += `## \u2705 Accepted Answer (${answerData.items[0].score} pts):
3264
+ ${cleanContent(cleanAnswer, 2e3)}
3265
+ `;
3266
+ }
3267
+ } catch {
3268
+ }
3269
+ }
3171
3270
  results.push({
3172
3271
  title: item.title || "",
3173
3272
  url: item.link || "",
3174
3273
  source: "stackoverflow",
3175
- snippet: `${item.score} votes, ${item.answer_count} answers${item.is_answered ? " \u2713" : ""}`,
3176
- score: item.score
3274
+ content: cleanContent(content),
3275
+ score: item.score,
3276
+ metadata: {
3277
+ author: item.owner?.display_name,
3278
+ date: item.creation_date ? new Date(item.creation_date * 1e3).toISOString().split("T")[0] : void 0,
3279
+ is_answered: item.is_answered
3280
+ }
3177
3281
  });
3178
3282
  }
3179
3283
  }
@@ -3182,15 +3286,6 @@ async function searchStackOverflow(query, limit) {
3182
3286
  }
3183
3287
  return results;
3184
3288
  }
3185
- function getXSearchUrl(query) {
3186
- const encodedQuery = encodeURIComponent(query);
3187
- return {
3188
- title: `Search X for: "${query}"`,
3189
- url: `https://twitter.com/search?q=${encodedQuery}&src=typed_query&f=live`,
3190
- source: "x",
3191
- snippet: "X/Twitter requires authentication. Click the link to search manually."
3192
- };
3193
- }
3194
3289
  async function searchWeb(args) {
3195
3290
  try {
3196
3291
  const {
@@ -3224,9 +3319,6 @@ async function searchWeb(args) {
3224
3319
  case "stackoverflow":
3225
3320
  searches.push(searchStackOverflow(query, resultsPerSource));
3226
3321
  break;
3227
- case "x":
3228
- allResults.push(getXSearchUrl(query));
3229
- break;
3230
3322
  }
3231
3323
  }
3232
3324
  const searchResults = await Promise.all(searches);
@@ -3240,8 +3332,7 @@ async function searchWeb(args) {
3240
3332
  query,
3241
3333
  results: limitedResults,
3242
3334
  sources_searched: sourcesSearched,
3243
- total_results: limitedResults.length,
3244
- note: sources.includes("x") ? "X/Twitter requires manual search due to API restrictions" : void 0
3335
+ total_results: limitedResults.length
3245
3336
  };
3246
3337
  } catch (error) {
3247
3338
  return {
@@ -4644,8 +4735,8 @@ var ToolCallNormalizer = class {
4644
4735
  */
4645
4736
  static extractFromContent(content) {
4646
4737
  const results = [];
4647
- const cleanContent = content.replace(/```(?:json)?\s*([\s\S]*?)```/g, "$1");
4648
- const jsonMatches = this.extractJsonObjects(cleanContent);
4738
+ const cleanContent2 = content.replace(/```(?:json)?\s*([\s\S]*?)```/g, "$1");
4739
+ const jsonMatches = this.extractJsonObjects(cleanContent2);
4649
4740
  for (const jsonStr of jsonMatches) {
4650
4741
  try {
4651
4742
  const parsed = JSON.parse(jsonStr);
@@ -4956,7 +5047,8 @@ ${editData.error.display}`;
4956
5047
  "count_file_lines",
4957
5048
  "read_file_lines",
4958
5049
  "todo",
4959
- "load_skill"
5050
+ "load_skill",
5051
+ "search_web"
4960
5052
  ];
4961
5053
  const toolToCall = validToolCalls[0];
4962
5054
  const isSafeTool = autoApprovedTools.some((safeTool) => toolToCall.function.name.includes(safeTool));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nomad-e/bluma-cli",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "BluMa independent agent for automation and advanced software engineering.",
5
5
  "author": "Alex Fonseca",
6
6
  "license": "Apache-2.0",