@nomad-e/bluma-cli 0.0.113 → 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.
- package/dist/main.js +152 -29
- 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 =
|
|
3075
|
-
var REQUEST_TIMEOUT =
|
|
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
|
-
|
|
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
|
-
|
|
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&
|
|
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(/</g, "<").replace(/>/g, ">").replace(/&/g, "&").replace(/"/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(/</g, "<").replace(/>/g, ">").replace(/&/g, "&").replace(/"/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
|
-
|
|
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 {
|
|
@@ -4023,6 +4114,37 @@ var SYSTEM_PROMPT = `
|
|
|
4023
4114
|
|
|
4024
4115
|
---
|
|
4025
4116
|
|
|
4117
|
+
<skills_knowledge>
|
|
4118
|
+
## Skills vs Base Knowledge (CRITICAL)
|
|
4119
|
+
|
|
4120
|
+
**You have TWO types of knowledge:**
|
|
4121
|
+
|
|
4122
|
+
1. **Base Training Knowledge** - General programming knowledge from your training:
|
|
4123
|
+
- Testing patterns, Git workflows, Docker, React, Python, etc.
|
|
4124
|
+
- This is your GENERAL EXPERTISE
|
|
4125
|
+
- This knowledge is ALWAYS available
|
|
4126
|
+
- This is NOT loadable via \`load_skill\` - it's already in you
|
|
4127
|
+
|
|
4128
|
+
2. **Pluggable Skills** - Specialized modules the user installs:
|
|
4129
|
+
- Listed in the \`<available_skills>\` section (if present)
|
|
4130
|
+
- These are LOADABLE via \`load_skill\` tool
|
|
4131
|
+
- These override/extend your base knowledge with project-specific workflows
|
|
4132
|
+
- These are files in \`.bluma/skills/\` or \`~/.bluma/skills/\`
|
|
4133
|
+
|
|
4134
|
+
**IMPORTANT RULES:**
|
|
4135
|
+
|
|
4136
|
+
- If you see \`<available_skills>\` section below \u2192 those are your pluggable skills
|
|
4137
|
+
- If you DON'T see \`<available_skills>\` section \u2192 you have NO pluggable skills installed
|
|
4138
|
+
- When asked "what skills do you have?":
|
|
4139
|
+
- If \`<available_skills>\` exists: list ONLY what's there
|
|
4140
|
+
- If \`<available_skills>\` doesn't exist: say "No pluggable skills installed. I have general knowledge about programming, testing, git, etc. but no specialized skills loaded."
|
|
4141
|
+
- NEVER invent skill names that aren't in \`<available_skills>\`
|
|
4142
|
+
- NEVER try to \`load_skill\` with a name that isn't in \`<available_skills>\`
|
|
4143
|
+
- Your base knowledge (testing, git, docker...) is NOT a skill - it's just knowledge you have
|
|
4144
|
+
</skills_knowledge>
|
|
4145
|
+
|
|
4146
|
+
---
|
|
4147
|
+
|
|
4026
4148
|
<environment>
|
|
4027
4149
|
## Machine Context (Your Deep Knowledge)
|
|
4028
4150
|
|
|
@@ -4070,7 +4192,7 @@ You MUST adapt all commands to this environment. Use the correct package manager
|
|
|
4070
4192
|
- No TODO needed for single operations
|
|
4071
4193
|
|
|
4072
4194
|
### Testing Philosophy:
|
|
4073
|
-
Run tests when modifying code.
|
|
4195
|
+
Run tests when modifying code. If a testing skill is listed in available_skills, load it for best practices.
|
|
4074
4196
|
|
|
4075
4197
|
**Commands:**
|
|
4076
4198
|
- Run tests: {test_command}
|
|
@@ -4613,8 +4735,8 @@ var ToolCallNormalizer = class {
|
|
|
4613
4735
|
*/
|
|
4614
4736
|
static extractFromContent(content) {
|
|
4615
4737
|
const results = [];
|
|
4616
|
-
const
|
|
4617
|
-
const jsonMatches = this.extractJsonObjects(
|
|
4738
|
+
const cleanContent2 = content.replace(/```(?:json)?\s*([\s\S]*?)```/g, "$1");
|
|
4739
|
+
const jsonMatches = this.extractJsonObjects(cleanContent2);
|
|
4618
4740
|
for (const jsonStr of jsonMatches) {
|
|
4619
4741
|
try {
|
|
4620
4742
|
const parsed = JSON.parse(jsonStr);
|
|
@@ -4925,7 +5047,8 @@ ${editData.error.display}`;
|
|
|
4925
5047
|
"count_file_lines",
|
|
4926
5048
|
"read_file_lines",
|
|
4927
5049
|
"todo",
|
|
4928
|
-
"load_skill"
|
|
5050
|
+
"load_skill",
|
|
5051
|
+
"search_web"
|
|
4929
5052
|
];
|
|
4930
5053
|
const toolToCall = validToolCalls[0];
|
|
4931
5054
|
const isSafeTool = autoApprovedTools.some((safeTool) => toolToCall.function.name.includes(safeTool));
|