jawere 1.5.1 → 1.6.0

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/index.js +360 -1
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -13101,6 +13101,14 @@ You have access to a set of tools that let you interact with the filesystem:
13101
13101
  Skips hidden dirs and node_modules, .git, dist, etc.
13102
13102
  grep Search file contents with regex. Returns file paths with line numbers.
13103
13103
  Skips binary files and files over 500KB.
13104
+ web_search Search the web for general information using DuckDuckGo (free).
13105
+ Returns abstracts, answers, definitions, related topics, web links.
13106
+ Use for general knowledge, news, current events \u2014 NOT for code docs.
13107
+ docs Search library/framework/API documentation specifically. Uses site-scoped
13108
+ DuckDuckGo queries targeting official doc sites (MDN, nodejs.org, docs.rs,
13109
+ react.dev, python.org, etc.). Optional library param narrows the search.
13110
+ Use this for API references, method signatures, config options, examples.
13111
+ Prefer this over web_search for any programming documentation lookup.
13104
13112
 
13105
13113
  \u2500\u2500 Parallel Tool Execution \u2500\u2500
13106
13114
 
@@ -13116,6 +13124,9 @@ independent of each other. This is faster and saves tokens \u2014 use it wheneve
13116
13124
  GOOD \u2014 read a file AND list a directory at the same time:
13117
13125
  \u2022 Call read(config.ts) + ls(src/) together
13118
13126
 
13127
+ GOOD \u2014 search docs/web while also reading files:
13128
+ \u2022 Call docs(query, library) + web_search(query) + read(file) all at once
13129
+
13119
13130
  BAD \u2014 these depend on each other, so must be sequential:
13120
13131
  \u2022 grep then read (grep finds a file, then you read it)
13121
13132
  \u2022 bash then read (you run a command, then read its output file)
@@ -13182,6 +13193,7 @@ import { exec } from "child_process";
13182
13193
  import { readFile, writeFile, mkdir, access, stat as fsStat } from "fs/promises";
13183
13194
  import { constants } from "fs";
13184
13195
  import { resolve, dirname } from "path";
13196
+ import { get as httpsGet } from "https";
13185
13197
  var TOOL_DEFS = [
13186
13198
  {
13187
13199
  type: "function",
@@ -13387,6 +13399,52 @@ var TOOL_DEFS = [
13387
13399
  required: ["pattern"]
13388
13400
  }
13389
13401
  }
13402
+ },
13403
+ {
13404
+ type: "function",
13405
+ function: {
13406
+ name: "web_search",
13407
+ description: "Search the web for information. Uses DuckDuckGo (free, no API key needed). Returns relevant results including abstracts, answers, related topics, and web links. Use this for general knowledge, news, current events, or broad information not found locally.",
13408
+ parameters: {
13409
+ type: "object",
13410
+ properties: {
13411
+ query: {
13412
+ type: "string",
13413
+ description: "Search query string"
13414
+ },
13415
+ count: {
13416
+ type: "number",
13417
+ description: "Maximum number of results to return (default 5, max 10)"
13418
+ }
13419
+ },
13420
+ required: ["query"]
13421
+ }
13422
+ }
13423
+ },
13424
+ {
13425
+ type: "function",
13426
+ function: {
13427
+ name: "docs",
13428
+ description: "Search library, framework, and API documentation. Uses DuckDuckGo site-targeted queries to search official documentation sources (MDN, Node.js docs, npm packages, Rust docs, Python docs, Go docs, etc.). Free \u2014 no API key needed. Use this for API signatures, method references, configuration options, package usage examples, or any programming documentation lookup.",
13429
+ parameters: {
13430
+ type: "object",
13431
+ properties: {
13432
+ query: {
13433
+ type: "string",
13434
+ description: 'Documentation search query (e.g. "fs.readFile options", "React useEffect cleanup")'
13435
+ },
13436
+ library: {
13437
+ type: "string",
13438
+ description: 'Optional library/package name to narrow search (e.g. "react", "typescript", "express")'
13439
+ },
13440
+ count: {
13441
+ type: "number",
13442
+ description: "Maximum results (default 5, max 8)"
13443
+ }
13444
+ },
13445
+ required: ["query"]
13446
+ }
13447
+ }
13390
13448
  }
13391
13449
  ];
13392
13450
  var MAX_OUTPUT_LINES = 2e3;
@@ -13789,6 +13847,285 @@ async function diffTool(path, staged, base, workDir) {
13789
13847
  else args.push("--", ".");
13790
13848
  return execBash(`git ${args.join(" ")}`, workDir, 30);
13791
13849
  }
13850
+ function httpsGetJSON(url, timeout = 8e3) {
13851
+ return new Promise((resolve4, reject) => {
13852
+ const req = httpsGet(url, { timeout }, (res) => {
13853
+ if (res.statusCode && res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
13854
+ httpsGetJSON(res.headers.location, timeout).then(resolve4).catch(reject);
13855
+ return;
13856
+ }
13857
+ let data = "";
13858
+ res.on("data", (chunk) => {
13859
+ data += chunk.toString();
13860
+ if (data.length > 5e5) {
13861
+ res.destroy();
13862
+ reject(new Error("Response too large"));
13863
+ }
13864
+ });
13865
+ res.on("end", () => {
13866
+ try {
13867
+ resolve4(JSON.parse(data));
13868
+ } catch {
13869
+ reject(new Error(`Failed to parse JSON (status ${res.statusCode})`));
13870
+ }
13871
+ });
13872
+ });
13873
+ req.on("error", reject);
13874
+ req.on("timeout", () => {
13875
+ req.destroy();
13876
+ reject(new Error("Request timed out"));
13877
+ });
13878
+ });
13879
+ }
13880
+ async function webSearchTool(query, count) {
13881
+ const maxResults = Math.min(Math.max(count ?? 5, 1), 10);
13882
+ const encoded = encodeURIComponent(query);
13883
+ const ddgUrl = `https://api.duckduckgo.com/?q=${encoded}&format=json&no_html=1&skip_disambig=1`;
13884
+ let json;
13885
+ try {
13886
+ json = await httpsGetJSON(ddgUrl, 1e4);
13887
+ } catch (err) {
13888
+ return `Error: Web search failed \u2014 ${err.message || err}`;
13889
+ }
13890
+ const parts = [];
13891
+ if (json.Heading && json.Heading !== query) {
13892
+ parts.push(`Topic: ${json.Heading}`);
13893
+ }
13894
+ if (json.Answer) {
13895
+ parts.push(`Answer: ${json.Answer}`);
13896
+ }
13897
+ if (json.AbstractText && json.AbstractText.trim()) {
13898
+ let abstract = json.AbstractText.trim();
13899
+ if (json.AbstractSource) {
13900
+ abstract += ` [Source: ${json.AbstractSource}]`;
13901
+ }
13902
+ if (json.AbstractURL) {
13903
+ abstract += ` \u2014 ${json.AbstractURL}`;
13904
+ }
13905
+ parts.push(`Summary: ${abstract}`);
13906
+ } else if (json.Abstract && json.Abstract.trim()) {
13907
+ parts.push(`Summary: ${json.Abstract.trim()}`);
13908
+ }
13909
+ if (json.Definition) {
13910
+ let def = `Definition: ${json.Definition}`;
13911
+ if (json.DefinitionSource) def += ` [Source: ${json.DefinitionSource}]`;
13912
+ parts.push(def);
13913
+ }
13914
+ if (json.Infobox?.content) {
13915
+ const fields = json.Infobox.content.filter((c2) => c2.label && c2.value).map((c2) => ` ${c2.label}: ${c2.value}`).slice(0, 8);
13916
+ if (fields.length > 0) {
13917
+ parts.push(`Details:
13918
+ ${fields.join("\n")}`);
13919
+ }
13920
+ }
13921
+ if (json.RelatedTopics && json.RelatedTopics.length > 0) {
13922
+ const topics = [];
13923
+ for (const t2 of json.RelatedTopics) {
13924
+ if (topics.length >= maxResults) break;
13925
+ if (t2.Text && t2.FirstURL) {
13926
+ const cleanText = t2.Text.replace(/<[^>]+>/g, "").trim();
13927
+ topics.push(` - ${cleanText.slice(0, 200)}
13928
+ ${t2.FirstURL}`);
13929
+ } else if (t2.Text) {
13930
+ topics.push(` - ${t2.Text.replace(/<[^>]+>/g, "").trim().slice(0, 200)}`);
13931
+ }
13932
+ }
13933
+ if (topics.length > 0) {
13934
+ parts.push(`Related:
13935
+ ${topics.join("\n")}`);
13936
+ }
13937
+ }
13938
+ if (json.Results && json.Results.length > 0) {
13939
+ const results = [];
13940
+ for (const r2 of json.Results) {
13941
+ if (results.length >= maxResults) break;
13942
+ if (r2.Text && r2.FirstURL) {
13943
+ results.push(` - ${r2.Text.replace(/<[^>]+>/g, "").trim().slice(0, 200)}
13944
+ ${r2.FirstURL}`);
13945
+ }
13946
+ }
13947
+ if (results.length > 0) {
13948
+ parts.push(`Web Results:
13949
+ ${results.join("\n")}`);
13950
+ }
13951
+ }
13952
+ if (parts.length === 0) {
13953
+ return `No results found for "${query}". Try different keywords or be more specific.`;
13954
+ }
13955
+ return truncateOutput(parts.join("\n\n")).text;
13956
+ }
13957
+ var KNOWN_DOC_SITES = [
13958
+ // JavaScript/TypeScript ecosystem
13959
+ { patterns: ["node", "nodejs", "node.js"], sites: ["nodejs.org", "nodejs.dev"] },
13960
+ { patterns: ["javascript", "js", "mozilla", "mdn"], sites: ["developer.mozilla.org"] },
13961
+ { patterns: ["typescript", "ts"], sites: ["typescriptlang.org"] },
13962
+ { patterns: ["react", "reactjs", "react.js"], sites: ["react.dev", "reactjs.org", "legacy.reactjs.org"] },
13963
+ { patterns: ["express", "expressjs", "express.js"], sites: ["expressjs.com"] },
13964
+ { patterns: ["next", "nextjs", "next.js"], sites: ["nextjs.org"] },
13965
+ { patterns: ["vue", "vuejs", "vue.js"], sites: ["vuejs.org"] },
13966
+ { patterns: ["svelte", "sveltekit"], sites: ["svelte.dev", "kit.svelte.dev"] },
13967
+ { patterns: ["tailwind", "tailwindcss"], sites: ["tailwindcss.com"] },
13968
+ { patterns: ["prisma"], sites: ["prisma.io"] },
13969
+ { patterns: ["vite"], sites: ["vitejs.dev"] },
13970
+ { patterns: ["esbuild"], sites: ["esbuild.github.io"] },
13971
+ { patterns: ["webpack"], sites: ["webpack.js.org"] },
13972
+ { patterns: ["babel"], sites: ["babeljs.io"] },
13973
+ { patterns: ["eslint"], sites: ["eslint.org"] },
13974
+ { patterns: ["prettier"], sites: ["prettier.io"] },
13975
+ { patterns: ["jest"], sites: ["jestjs.io"] },
13976
+ { patterns: ["vitest"], sites: ["vitest.dev"] },
13977
+ { patterns: ["playwright"], sites: ["playwright.dev"] },
13978
+ { patterns: ["cypress"], sites: ["cypress.io", "docs.cypress.io"] },
13979
+ { patterns: ["graphql"], sites: ["graphql.org"] },
13980
+ { patterns: ["apollo"], sites: ["apollographql.com"] },
13981
+ { patterns: ["trpc"], sites: ["trpc.io"] },
13982
+ { patterns: ["zod"], sites: ["github.com/colinhacks/zod", "zod.dev"] },
13983
+ { patterns: ["axios"], sites: ["axios-http.com"] },
13984
+ { patterns: ["npm"], sites: ["npmjs.com", "docs.npmjs.com"] },
13985
+ { patterns: ["pnpm"], sites: ["pnpm.io"] },
13986
+ { patterns: ["yarn"], sites: ["yarnpkg.com"] },
13987
+ { patterns: ["deno"], sites: ["deno.com", "deno.land", "docs.deno.com"] },
13988
+ { patterns: ["bun"], sites: ["bun.sh"] },
13989
+ // Python
13990
+ { patterns: ["python", "py"], sites: ["docs.python.org"] },
13991
+ { patterns: ["django"], sites: ["djangoproject.com", "docs.djangoproject.com"] },
13992
+ { patterns: ["flask"], sites: ["flask.palletsprojects.com"] },
13993
+ { patterns: ["fastapi"], sites: ["fastapi.tiangolo.com"] },
13994
+ { patterns: ["pydantic"], sites: ["docs.pydantic.dev"] },
13995
+ { patterns: ["pytest"], sites: ["docs.pytest.org"] },
13996
+ { patterns: ["poetry"], sites: ["python-poetry.org"] },
13997
+ { patterns: ["numpy"], sites: ["numpy.org"] },
13998
+ { patterns: ["pandas"], sites: ["pandas.pydata.org"] },
13999
+ { patterns: ["sqlalchemy"], sites: ["docs.sqlalchemy.org"] },
14000
+ { patterns: ["alembic"], sites: ["alembic.sqlalchemy.org"] },
14001
+ // Rust
14002
+ { patterns: ["rust", "cargo", "rustc"], sites: ["doc.rust-lang.org", "docs.rs"] },
14003
+ { patterns: ["tokio"], sites: ["docs.rs/tokio", "tokio.rs"] },
14004
+ { patterns: ["serde"], sites: ["docs.rs/serde", "serde.rs"] },
14005
+ { patterns: ["actix"], sites: ["actix.rs", "docs.rs/actix-web"] },
14006
+ { patterns: ["axum"], sites: ["docs.rs/axum"] },
14007
+ { patterns: ["bevy"], sites: ["bevyengine.org", "docs.rs/bevy"] },
14008
+ // Go
14009
+ { patterns: ["go", "golang"], sites: ["pkg.go.dev", "go.dev"] },
14010
+ { patterns: ["gin"], sites: ["gin-gonic.com", "pkg.go.dev/github.com/gin-gonic"] },
14011
+ { patterns: ["echo"], sites: ["echo.labstack.com", "pkg.go.dev/github.com/labstack/echo"] },
14012
+ { patterns: ["fiber"], sites: ["docs.gofiber.io", "pkg.go.dev/github.com/gofiber/fiber"] },
14013
+ // Ruby
14014
+ { patterns: ["ruby", "rubygems"], sites: ["ruby-doc.org", "rubygems.org"] },
14015
+ { patterns: ["rails", "ruby on rails"], sites: ["guides.rubyonrails.org", "api.rubyonrails.org"] },
14016
+ { patterns: ["rspec"], sites: ["rspec.info", "rubydoc.info"] },
14017
+ // Other
14018
+ { patterns: ["linux", "man"], sites: ["man7.org", "linux.die.net"] },
14019
+ { patterns: ["git"], sites: ["git-scm.com"] },
14020
+ { patterns: ["docker"], sites: ["docs.docker.com"] },
14021
+ { patterns: ["kubernetes", "k8s"], sites: ["kubernetes.io"] },
14022
+ { patterns: ["nginx"], sites: ["nginx.org", "nginx.com"] },
14023
+ { patterns: ["postgresql", "postgres", "pg"], sites: ["postgresql.org"] },
14024
+ { patterns: ["mysql"], sites: ["dev.mysql.com"] },
14025
+ { patterns: ["redis"], sites: ["redis.io"] },
14026
+ { patterns: ["mongodb", "mongo"], sites: ["mongodb.com", "docs.mongodb.com"] },
14027
+ { patterns: ["sqlite"], sites: ["sqlite.org"] },
14028
+ { patterns: ["aws"], sites: ["docs.aws.amazon.com"] }
14029
+ // Add more as needed — this list guides the agent to official docs
14030
+ ];
14031
+ function resolveDocSites(query, library) {
14032
+ const searchTerms = (library ? `${library} ${query}` : query).toLowerCase();
14033
+ const sites = [];
14034
+ for (const entry of KNOWN_DOC_SITES) {
14035
+ for (const pat of entry.patterns) {
14036
+ if (searchTerms.includes(pat) && !sites.some((s2) => entry.sites.includes(s2))) {
14037
+ sites.push(...entry.sites);
14038
+ break;
14039
+ }
14040
+ }
14041
+ }
14042
+ return sites;
14043
+ }
14044
+ async function docsSearchTool(query, library, count, useSites = true) {
14045
+ const maxResults = Math.min(Math.max(count ?? 5, 1), 8);
14046
+ const sites = useSites ? resolveDocSites(query, library) : [];
14047
+ let searchQuery;
14048
+ if (library) {
14049
+ searchQuery = `${library} ${query}`;
14050
+ } else {
14051
+ searchQuery = query;
14052
+ }
14053
+ if (sites.length > 0) {
14054
+ const siteFilters = sites.map((s2) => `site:${s2}`).join(" OR ");
14055
+ searchQuery = `${searchQuery} (${siteFilters})`;
14056
+ } else {
14057
+ searchQuery = `${searchQuery} documentation`;
14058
+ }
14059
+ const ddgUrl = `https://api.duckduckgo.com/?q=${encodeURIComponent(searchQuery)}&format=json&no_html=1&skip_disambig=1`;
14060
+ let json;
14061
+ try {
14062
+ json = await httpsGetJSON(ddgUrl, 1e4);
14063
+ } catch (err) {
14064
+ return `Error: Docs search failed \u2014 ${err.message || err}`;
14065
+ }
14066
+ const parts = [];
14067
+ const headerParts = [];
14068
+ if (library) headerParts.push(`Library: ${library}`);
14069
+ if (sites.length > 0) headerParts.push(`Sources: ${sites.join(", ")}`);
14070
+ if (headerParts.length > 0) {
14071
+ parts.push(headerParts.join(" | "));
14072
+ }
14073
+ if (json.Answer) {
14074
+ parts.push(`Answer: ${json.Answer}`);
14075
+ }
14076
+ if (json.AbstractText && json.AbstractText.trim()) {
14077
+ const abstract = json.AbstractText.trim();
14078
+ const source = json.AbstractSource ? ` [${json.AbstractSource}]` : "";
14079
+ const url = json.AbstractURL ? ` \u2014 ${json.AbstractURL}` : "";
14080
+ parts.push(`Summary: ${abstract}${source}${url}`);
14081
+ } else if (json.Abstract && json.Abstract.trim()) {
14082
+ parts.push(`Summary: ${json.Abstract.trim()}`);
14083
+ }
14084
+ if (json.Definition) {
14085
+ let def = `Definition: ${json.Definition}`;
14086
+ if (json.DefinitionSource) def += ` [Source: ${json.DefinitionSource}]`;
14087
+ parts.push(def);
14088
+ }
14089
+ if (json.RelatedTopics && json.RelatedTopics.length > 0) {
14090
+ const topics = [];
14091
+ for (const t2 of json.RelatedTopics) {
14092
+ if (topics.length >= maxResults) break;
14093
+ if (t2.Text && t2.FirstURL) {
14094
+ const cleanText = t2.Text.replace(/<[^>]+>/g, "").trim();
14095
+ topics.push(` - ${cleanText.slice(0, 250)}
14096
+ ${t2.FirstURL}`);
14097
+ } else if (t2.Text) {
14098
+ topics.push(` - ${t2.Text.replace(/<[^>]+>/g, "").trim().slice(0, 250)}`);
14099
+ }
14100
+ }
14101
+ if (topics.length > 0) {
14102
+ parts.push(`Documentation Results:
14103
+ ${topics.join("\n")}`);
14104
+ }
14105
+ }
14106
+ if (json.Results && json.Results.length > 0) {
14107
+ const results = [];
14108
+ for (const r2 of json.Results) {
14109
+ if (results.length >= maxResults) break;
14110
+ if (r2.Text && r2.FirstURL) {
14111
+ results.push(` - ${r2.Text.replace(/<[^>]+>/g, "").trim().slice(0, 200)}
14112
+ ${r2.FirstURL}`);
14113
+ }
14114
+ }
14115
+ if (results.length > 0) {
14116
+ parts.push(`More:
14117
+ ${results.join("\n")}`);
14118
+ }
14119
+ }
14120
+ const hasContent = parts.length > (headerParts.length > 0 ? 1 : 0);
14121
+ if (!hasContent && sites.length > 0) {
14122
+ return docsSearchTool(query, library, count, false);
14123
+ }
14124
+ if (!hasContent) {
14125
+ return `No documentation results for "${query}". Try different terms or use web_search for broader results.`;
14126
+ }
14127
+ return truncateOutput(parts.join("\n\n")).text;
14128
+ }
13792
14129
  async function executeTool(call, workDir) {
13793
14130
  const { id, function: fn } = call;
13794
14131
  let args;
@@ -13850,6 +14187,19 @@ async function executeTool(call, workDir) {
13850
14187
  case "stat":
13851
14188
  result = await statTool(args.path, workDir);
13852
14189
  break;
14190
+ case "web_search":
14191
+ result = await webSearchTool(
14192
+ args.query,
14193
+ args.count
14194
+ );
14195
+ break;
14196
+ case "docs":
14197
+ result = await docsSearchTool(
14198
+ args.query,
14199
+ args.library,
14200
+ args.count
14201
+ );
14202
+ break;
13853
14203
  default:
13854
14204
  result = `Error: Unknown tool: ${fn.name}`;
13855
14205
  }
@@ -14312,11 +14662,20 @@ var COL = () => process.stdout.columns || 80;
14312
14662
  var GRUVBOX_GREEN = "\x1B[38;2;184;187;3m";
14313
14663
  var GRUVBOX_GRAY = "\x1B[38;2;146;131;116m";
14314
14664
  var GRUVBOX_RED = "\x1B[38;2;251;73;52m";
14665
+ var GRUVBOX_AQUA = "\x1B[38;2;142;192;124m";
14315
14666
  var RESET2 = "\x1B[0m";
14316
14667
  var FILE_TOOLS = /* @__PURE__ */ new Set(["read", "write", "edit", "stat"]);
14668
+ var SEARCH_TOOLS = /* @__PURE__ */ new Set(["web_search", "docs"]);
14317
14669
  var SILENT_TOOLS = /* @__PURE__ */ new Set(["diff"]);
14318
14670
  function toolDetail(name, args) {
14319
14671
  switch (name) {
14672
+ case "web_search":
14673
+ return String(args.query || "?");
14674
+ case "docs": {
14675
+ let d2 = String(args.query || "?");
14676
+ if (args.library) d2 = `${args.library}: ${d2}`;
14677
+ return d2;
14678
+ }
14320
14679
  case "read": {
14321
14680
  let d2 = String(args.path || "?");
14322
14681
  if (args.offset) d2 += ` [L${args.offset}${args.limit ? `-${Number(args.offset) + Number(args.limit) - 1}` : "+"}]`;
@@ -14345,7 +14704,7 @@ function displayToolLine(name, args, ok, spin) {
14345
14704
  }
14346
14705
  const statusIcon = ok ? "\u2713" : "\u2717";
14347
14706
  const statusColor = ok ? GRUVBOX_GREEN : GRUVBOX_RED;
14348
- const toolColor = FILE_TOOLS.has(name) ? GRUVBOX_GREEN : GRUVBOX_GRAY;
14707
+ const toolColor = SEARCH_TOOLS.has(name) ? GRUVBOX_AQUA : FILE_TOOLS.has(name) ? GRUVBOX_GREEN : GRUVBOX_GRAY;
14349
14708
  const prefix = `${toolColor}${name}${RESET2}: `;
14350
14709
  const suffix = ` ${statusColor}${statusIcon}${RESET2}`;
14351
14710
  let detail = toolDetail(name, args);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jawere",
3
- "version": "1.5.1",
3
+ "version": "1.6.0",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "jawere": "bin/jawere.js"