nothumanallowed 13.2.81 → 13.2.82

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nothumanallowed",
3
- "version": "13.2.81",
3
+ "version": "13.2.82",
4
4
  "description": "NotHumanAllowed — 38 AI agents, 80 tools, Studio (visual agentic workflows). Email, calendar, browser automation, screen capture, canvas, cron/heartbeat, Alexandria E2E messaging, GitHub, Notion, Slack, voice chat, free AI (Liara), 28 languages. Zero-dependency CLI.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -2958,12 +2958,30 @@ ${rawText.slice(0, 18000)}`;
2958
2958
  // If there is document context from a previous step, ask the LLM to derive
2959
2959
  // the optimal search queries. This is generic and works for any document/task.
2960
2960
  let searchQueries = [stepPrompt.slice(0, 120)];
2961
- if (context && context.length > 50) {
2961
+ // Only use LLM query generation when context is a PDF/document (not previous agent text output).
2962
+ // When context is email/github output from a prior step, ignore it — use task + stepPrompt directly.
2963
+ const contextIsPdf = context && context.length > 50 && context.startsWith('## ATTACHED PDF');
2964
+ if (contextIsPdf) {
2962
2965
  sendToken('[Building search queries from document...] ');
2963
2966
  try {
2964
- // Document context goes in system prompt — SENTINEL only scans user message
2965
2967
  const queryPlanSys = `You are a search query generator. Given a document summary and a user task, output a JSON array of 1-3 concise web search queries (strings, max 80 chars each) that will find the best results. Output ONLY the JSON array, no explanation.\n\nDocument content:\n${context.slice(0, 3000)}`;
2966
- const queryPlanUser = `User task: "${task.slice(0, 200)}". Generate search queries. If task asks for similar/alternative products use technical specs. If it asks where to buy include vendor queries. Output: ["query1","query2",...]`;
2968
+ const queryPlanUser = `User task: "${task.slice(0, 200)}". Generate search queries. If task asks for similar/alternative products use technical specs. Output: ["query1","query2",...]`;
2969
+ const planConfig2 = Object.assign({}, config, { thinking: 'off' });
2970
+ const queryRaw = await withTimeout(callLLM(planConfig2, queryPlanSys, queryPlanUser, { max_tokens: 200 }), 15000);
2971
+ const jsonMatch = queryRaw.match(/\[[\s\S]*?\]/);
2972
+ if (jsonMatch) {
2973
+ const parsed = JSON.parse(jsonMatch[0]);
2974
+ if (Array.isArray(parsed) && parsed.length > 0) {
2975
+ searchQueries = parsed.filter(q => typeof q === 'string' && q.length > 2).slice(0, 3);
2976
+ }
2977
+ }
2978
+ } catch {}
2979
+ } else {
2980
+ // No PDF — derive queries from task + stepPrompt using LLM for better queries
2981
+ sendToken('[Building search queries...] ');
2982
+ try {
2983
+ const queryPlanSys = `You are a search query generator. Given a user task and a search instruction, output a JSON array of 2-3 concise web search queries (strings, max 80 chars each). Focus on the specific topics in the task. Output ONLY the JSON array, no explanation.`;
2984
+ const queryPlanUser = `Task: "${task.slice(0, 300)}"\nSearch instruction: "${stepPrompt.slice(0, 200)}"\nOutput: ["query1","query2","query3"]`;
2967
2985
  const planConfig2 = Object.assign({}, config, { thinking: 'off' });
2968
2986
  const queryRaw = await withTimeout(callLLM(planConfig2, queryPlanSys, queryPlanUser, { max_tokens: 200 }), 15000);
2969
2987
  const jsonMatch = queryRaw.match(/\[[\s\S]*?\]/);
@@ -2974,8 +2992,8 @@ ${rawText.slice(0, 18000)}`;
2974
2992
  }
2975
2993
  }
2976
2994
  } catch {}
2977
- sendToken(`[Queries: ${searchQueries.map(q => '"' + q + '"').join(', ')}] `);
2978
2995
  }
2996
+ sendToken(`[Queries: ${searchQueries.map(q => '"' + q + '"').join(', ')}] `);
2979
2997
 
2980
2998
  // Run all queries sequentially, accumulate results
2981
2999
  for (let qi = 0; qi < searchQueries.length; qi++) {
@@ -3012,24 +3030,64 @@ ${rawText.slice(0, 18000)}`;
3012
3030
  toolData = 'GitHub token not configured. Run: nha config set github-token YOUR_PAT';
3013
3031
  } else {
3014
3032
  const parts = [];
3015
- // Notifications (always available)
3016
- try {
3017
- const notifs = await withTimeout(gh.listNotifications(config, 15), 'GitHubAgent-notifs');
3018
- if (notifs) parts.push('## GitHub Notifications\n' + notifs);
3019
- } catch (e) { /* skip */ }
3020
- // Issues/PRs on configured repo if available
3021
- const repo = config.github?.defaultRepo || '';
3022
- if (repo) {
3033
+ // Extract repo from prompt or task (e.g. "owner/repo" pattern)
3034
+ const repoMatch = (stepPrompt + ' ' + task).match(/([a-zA-Z0-9_.-]+\/[a-zA-Z0-9_.-]+)/);
3035
+ const targetRepo = repoMatch ? repoMatch[1].replace(/[`'"]/g, '') : (config.github?.defaultRepo || '');
3036
+
3037
+ if (targetRepo) {
3038
+ sendToken(`[Analyzing ${targetRepo}...] `);
3039
+ // Repo metadata
3040
+ try {
3041
+ const info = await withTimeout(gh.getRepoInfo(config, targetRepo), 20000);
3042
+ parts.push(`## Repository: ${info.full_name}\n` +
3043
+ `- Description: ${info.description || 'none'}\n` +
3044
+ `- Stars: ${info.stars} | Forks: ${info.forks} | Watchers: ${info.watchers}\n` +
3045
+ `- Open issues: ${info.open_issues}\n` +
3046
+ `- Primary language: ${info.language}\n` +
3047
+ `- Topics: ${info.topics}\n` +
3048
+ `- License: ${info.license}\n` +
3049
+ `- Last push: ${info.pushed_at} | Created: ${info.created_at}\n` +
3050
+ `- Homepage: ${info.homepage || 'none'}\n` +
3051
+ `- Archived: ${info.archived}`);
3052
+ } catch (e) { parts.push(`## Repository ${targetRepo}\nCould not fetch repo info: ${e.message}`); }
3053
+ // Languages
3054
+ try {
3055
+ const langs = await withTimeout(gh.getRepoLanguages(config, targetRepo), 10000);
3056
+ if (langs) parts.push('## Languages\n' + langs);
3057
+ } catch {}
3058
+ // README
3023
3059
  try {
3024
- const issues = await withTimeout(gh.listIssues(config, repo, 'open', 10), 'GitHubAgent-issues');
3025
- if (issues) parts.push('## Open Issues (' + repo + ')\n' + issues);
3026
- } catch (e) { /* skip */ }
3060
+ const readme = await withTimeout(gh.getReadme(config, targetRepo), 15000);
3061
+ if (readme) parts.push('## README\n' + readme.slice(0, 3000));
3062
+ } catch {}
3063
+ // Recent commits
3064
+ try {
3065
+ const commits = await withTimeout(gh.getRecentCommits(config, targetRepo, 10), 15000);
3066
+ if (commits) parts.push('## Recent Commits\n' + commits);
3067
+ } catch {}
3068
+ // Open issues
3069
+ try {
3070
+ const issues = await withTimeout(gh.listIssues(config, targetRepo, 'open', 10), 15000);
3071
+ if (issues) parts.push('## Open Issues\n' + issues);
3072
+ } catch {}
3073
+ // Open PRs
3074
+ try {
3075
+ const prs = await withTimeout(gh.listPRs(config, targetRepo, 'open', 10), 15000);
3076
+ if (prs) parts.push('## Open Pull Requests\n' + prs);
3077
+ } catch {}
3078
+ // Contributors
3079
+ try {
3080
+ const contributors = await withTimeout(gh.getContributors(config, targetRepo, 10), 10000);
3081
+ if (contributors) parts.push('## Contributors\n' + contributors);
3082
+ } catch {}
3083
+ } else {
3084
+ // No specific repo — read notifications + user repos
3027
3085
  try {
3028
- const prs = await withTimeout(gh.listPRs(config, repo, 'open', 10), 'GitHubAgent-prs');
3029
- if (prs) parts.push('## Open PRs (' + repo + ')\n' + prs);
3030
- } catch (e) { /* skip */ }
3086
+ const notifs = await withTimeout(gh.listNotifications(config, 15), 15000);
3087
+ if (notifs) parts.push('## GitHub Notifications\n' + notifs);
3088
+ } catch {}
3031
3089
  }
3032
- toolData = parts.length > 0 ? parts.join('\n\n') : 'No GitHub data available.';
3090
+ toolData = parts.length > 0 ? parts.join('\n\n') : 'No GitHub data could be retrieved.';
3033
3091
  }
3034
3092
  } catch (e) { toolData = `GitHub read failed: ${e.message}`; }
3035
3093
 
@@ -3188,14 +3246,15 @@ ${task}
3188
3246
  CRITICAL RULES:
3189
3247
  - Do NOT output JSON, tool calls, function calls, or code blocks
3190
3248
  - NEVER invent, fabricate, or hallucinate data, events, emails, meetings, or news
3191
- - ONLY use the EXACT data provided in the DATA sections below if no data is provided, say so clearly
3192
- - Do NOT add fictional examples, placeholder content, or generic suggestions not grounded in the data
3249
+ - ONLY use data from the DATA sections that is RELEVANT to your specific domain and the WORKFLOW GOAL
3250
+ - If the previous agents' output contains irrelevant personal data (e.g. unrelated emails, purchases, subscriptions) — IGNORE it entirely
3251
+ - ONLY reference data that directly relates to the subject of the WORKFLOW GOAL
3252
+ - If genuinely no relevant data exists for your domain, say so clearly — do NOT invent analysis
3193
3253
  - Write in plain prose, structured with markdown headers (##) and bullet points (-)
3194
3254
  - Be thorough and specific — this is for an executive briefing based on REAL data only
3195
- - Always keep the OVERALL WORKFLOW GOAL in mind — apply your analysis specifically to the subject mentioned
3196
3255
 
3197
3256
  ${attachmentText ? `## ATTACHED FILE CONTENT:\n${attachmentText}\n` : ''}${toolData ? `## LIVE DATA FROM TOOLS:\n${toolData}\n` : '## LIVE DATA: No tool data was fetched for this step.\n'}
3198
- ${context ? `## OUTPUT FROM PREVIOUS AGENTS:\n${context}\n` : ''}`;
3257
+ ${context ? `## OUTPUT FROM PREVIOUS AGENTS (use only what is RELEVANT to the workflow goal):\n${context}\n` : ''}`;
3199
3258
  userMsg = hasRealData
3200
3259
  ? `Based ONLY on the real data above, complete this task specifically for the subject in the WORKFLOW GOAL: ${stepPrompt}`
3201
3260
  : `No real data is available for "${task}". State this clearly and explain what data would be needed to complete: ${stepPrompt}`;
package/src/constants.mjs CHANGED
@@ -5,7 +5,7 @@ import { fileURLToPath } from 'url';
5
5
  const __filename = fileURLToPath(import.meta.url);
6
6
  const __dirname = path.dirname(__filename);
7
7
 
8
- export const VERSION = '13.2.81';
8
+ export const VERSION = '13.2.82';
9
9
  export const BASE_URL = 'https://nothumanallowed.com/cli';
10
10
  export const API_BASE = 'https://nothumanallowed.com/api/v1';
11
11
 
@@ -185,6 +185,85 @@ export async function listUserRepos(config, maxResults = 30) {
185
185
  };
186
186
  }
187
187
 
188
+ /**
189
+ * Get repository metadata: description, stars, forks, language, topics, license, last push.
190
+ */
191
+ export async function getRepoInfo(config, repo) {
192
+ const data = await ghFetch(config, `/repos/${repo}`);
193
+ return {
194
+ full_name: data.full_name,
195
+ description: data.description || '',
196
+ stars: data.stargazers_count || 0,
197
+ forks: data.forks_count || 0,
198
+ watchers: data.watchers_count || 0,
199
+ open_issues: data.open_issues_count || 0,
200
+ language: data.language || 'N/A',
201
+ topics: (data.topics || []).join(', ') || 'none',
202
+ license: data.license?.name || 'none',
203
+ default_branch: data.default_branch || 'main',
204
+ pushed_at: data.pushed_at ? data.pushed_at.slice(0, 10) : 'unknown',
205
+ created_at: data.created_at ? data.created_at.slice(0, 10) : 'unknown',
206
+ homepage: data.homepage || '',
207
+ private: data.private,
208
+ archived: data.archived,
209
+ };
210
+ }
211
+
212
+ /**
213
+ * Get programming languages breakdown for a repo.
214
+ */
215
+ export async function getRepoLanguages(config, repo) {
216
+ const data = await ghFetch(config, `/repos/${repo}/languages`);
217
+ const total = Object.values(data).reduce((a, b) => a + b, 0) || 1;
218
+ return Object.entries(data)
219
+ .map(([lang, bytes]) => `${lang}: ${((bytes / total) * 100).toFixed(1)}%`)
220
+ .join(', ');
221
+ }
222
+
223
+ /**
224
+ * Get recent commits for a repo (last N).
225
+ */
226
+ export async function getRecentCommits(config, repo, maxResults = 10) {
227
+ const data = await ghFetch(config, `/repos/${repo}/commits?per_page=${maxResults}`);
228
+ if (!Array.isArray(data) || data.length === 0) return `No commits found in ${repo}.`;
229
+ return data.map((c, i) => {
230
+ const sha = c.sha ? c.sha.slice(0, 7) : '?';
231
+ const msg = (c.commit?.message || '').split('\n')[0].slice(0, 100);
232
+ const author = c.commit?.author?.name || c.author?.login || 'unknown';
233
+ const date = c.commit?.author?.date ? c.commit.author.date.slice(0, 10) : '';
234
+ return `${i + 1}. [${sha}] ${date} — ${author}: ${msg}`;
235
+ }).join('\n');
236
+ }
237
+
238
+ /**
239
+ * Get README content for a repo.
240
+ */
241
+ export async function getReadme(config, repo) {
242
+ try {
243
+ const data = await ghFetch(config, `/repos/${repo}/readme`);
244
+ if (data.content) {
245
+ const text = Buffer.from(data.content, 'base64').toString('utf-8');
246
+ return text.slice(0, 6000); // cap at 6KB
247
+ }
248
+ return '';
249
+ } catch {
250
+ return '';
251
+ }
252
+ }
253
+
254
+ /**
255
+ * Get contributors for a repo.
256
+ */
257
+ export async function getContributors(config, repo, maxResults = 10) {
258
+ try {
259
+ const data = await ghFetch(config, `/repos/${repo}/contributors?per_page=${maxResults}`);
260
+ if (!Array.isArray(data) || data.length === 0) return 'No contributors data.';
261
+ return data.map((c, i) => `${i + 1}. @${c.login} — ${c.contributions} commits`).join('\n');
262
+ } catch {
263
+ return 'Contributors data unavailable.';
264
+ }
265
+ }
266
+
188
267
  export async function markNotificationsRead(config) {
189
268
  await ghFetch(config, '/notifications', {
190
269
  method: 'PUT',