@fettstorch/clai 0.1.4 → 0.1.6

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/README.md CHANGED
@@ -33,9 +33,9 @@ clai "how tall can giraffes get?"
33
33
  clai how tall can giraffes get
34
34
  clai
35
35
  ```
36
- <img width="300" src="https://github.com/user-attachments/assets/f4a81e24-ef5b-42b7-bca7-188763d4e5cf" />
36
+ <img width="400" src="https://github.com/user-attachments/assets/002b3e05-5c77-4f4d-8aa3-ecb7412e9538" />
37
37
 
38
- The clie expects either a URL or a search query or no argument at all.
38
+ The cli expects either a URL or a search query or no argument at all.
39
39
  When passing a search query without quotes make sure not to use any special characters, that might confuse the CLI e.g. ?
40
40
 
41
41
  ### Programmatic API
package/dist/cli.js CHANGED
@@ -67837,8 +67837,6 @@ async function getGoogleResults(query) {
67837
67837
  const urlLower = url.toLowerCase();
67838
67838
  return !urlLower.includes("www.google") && !urlLower.includes("gstatic.com") && !urlLower.includes("googleapis.com") && !urlLower.includes("googleadservices") && queryWords.some((word) => urlLower.includes(word));
67839
67839
  }));
67840
- console.log("queryWords", queryWords);
67841
- console.log("filteredUrls", filteredUrls);
67842
67840
  const results = [...filteredUrls].slice(0, 3);
67843
67841
  if (results.length === 0) {
67844
67842
  throw new Error("No search results found");
@@ -67861,6 +67859,19 @@ function extractDataFromHtml(html3) {
67861
67859
  url: cheerioDoc('link[rel="canonical"]').attr("href") || ""
67862
67860
  };
67863
67861
  }
67862
+
67863
+ // node_modules/@fettstorch/jule/dist/esm/when.js
67864
+ function when(c) {
67865
+ return (cases) => {
67866
+ const elseHandler = cases.else;
67867
+ const safeAccessor = c;
67868
+ const handler = safeAccessor in cases ? cases[safeAccessor] : elseHandler;
67869
+ if (typeof handler === "function") {
67870
+ return handler(c);
67871
+ }
67872
+ return handler;
67873
+ };
67874
+ }
67864
67875
  // node_modules/@fettstorch/jule/dist/esm/observable.js
67865
67876
  class Observable {
67866
67877
  listeners = new Set;
@@ -72840,7 +72851,7 @@ function truncateContent(content) {
72840
72851
  const maxChars = MAX_INPUT_TOKENS * 4;
72841
72852
  if (content.length <= maxChars)
72842
72853
  return content;
72843
- return content.slice(0, maxChars) + "... (content truncated)";
72854
+ return content.slice(0, maxChars);
72844
72855
  }
72845
72856
 
72846
72857
  class OpenAIWrapper {
@@ -72862,8 +72873,8 @@ class OpenAIWrapper {
72862
72873
  async completeStructured(prompt2, options) {
72863
72874
  const truncatedPrompt = truncateContent(prompt2);
72864
72875
  const {
72865
- model = "gpt-3.5-turbo",
72866
- temperature = 0.6,
72876
+ model = "gpt-4o-mini",
72877
+ temperature = 1.6,
72867
72878
  functionName = "generate_response",
72868
72879
  responseSchema
72869
72880
  } = options;
@@ -72914,6 +72925,7 @@ async function summarizeWebPage(content, openAIApiKey) {
72914
72925
  - Step 2: Do that
72915
72926
  - Step 3: Done
72916
72927
  10. Mark proper nouns as bold e.g. **Harry Potter**
72928
+ 11. Mark headings (h1, h2, h3) as #, ##, ### respectively
72917
72929
 
72918
72930
  Don't just summarize, cite the key information.
72919
72931
 
@@ -72959,12 +72971,51 @@ async function clai(input, openAIKey) {
72959
72971
  sources: scrapedData.map((data2) => data2.url)
72960
72972
  };
72961
72973
  }
72974
+ // package.json
72975
+ var package_default = {
72976
+ name: "@fettstorch/clai",
72977
+ version: "0.1.6",
72978
+ main: "dist/index.js",
72979
+ bin: {
72980
+ clai: "dist/cli.js"
72981
+ },
72982
+ repository: {
72983
+ type: "git",
72984
+ url: "git+https://github.com/schnullerpip/clai.git"
72985
+ },
72986
+ scripts: {
72987
+ start: "bun run src/cli.ts",
72988
+ build: "bun build ./src/index.ts --outdir dist --target node && bun build ./src/cli.ts --outdir dist --target node",
72989
+ dev: "bun --watch src/cli.ts"
72990
+ },
72991
+ author: "schnullerpip (https://github.com/schnullerpip)",
72992
+ license: "ISC",
72993
+ description: "AI-powered webpage summarizer",
72994
+ dependencies: {
72995
+ "@fettstorch/jule": "^0.5.3",
72996
+ chalk: "^5.3.0",
72997
+ cheerio: "^1.0.0-rc.12",
72998
+ commander: "^12.1.0",
72999
+ inquirer: "^12.1.0",
73000
+ openai: "^4.73.0",
73001
+ ora: "^8.1.1",
73002
+ googleapis: "^126.0.1"
73003
+ },
73004
+ devDependencies: {
73005
+ "@types/inquirer": "^9.0.7",
73006
+ "@types/node": "^20.11.19",
73007
+ "bun-types": "latest"
73008
+ },
73009
+ publishConfig: {
73010
+ access: "public"
73011
+ }
73012
+ };
72962
73013
 
72963
73014
  // src/cli.ts
72964
73015
  var program2 = new Command;
72965
73016
  async function main2() {
72966
73017
  try {
72967
- program2.name("clai").description("AI-powered web scraping tool").version("1.0.0").argument("[input...]", "URL or search terms to analyze").action(async (inputs) => {
73018
+ program2.name("clai").description("AI-powered web scraping tool").version(package_default.version).argument("[input...]", "URL or search terms to analyze").action(async (inputs) => {
72968
73019
  const openAIKey = process.env.OPENAI_API_KEY;
72969
73020
  if (!openAIKey) {
72970
73021
  console.error(source_default.red("\u274C OPENAI_API_KEY environment variable is not set"));
@@ -73019,15 +73070,22 @@ async function animateText(text3, delay = 25) {
73019
73070
  process.stdout.write("\n");
73020
73071
  }
73021
73072
  function formatMarkdownForTerminal(text3) {
73022
- return text3.replace(/\*\*(.*?)\*\*/g, (_3, content) => source_default.bold(content));
73073
+ const headingHandled = text3.replace(/^(#{1,3})\s+(.*?)$/gm, (_3, hashes, content) => when(hashes.length)({
73074
+ 1: () => `\n${source_default.yellow.bold("\u2550\u2550\u2550 ")}${source_default.yellow.bold(content)}${source_default.yellow.bold(" \u2550\u2550\u2550")}`,
73075
+ 2: () => source_default.yellowBright.bold(content),
73076
+ 3: () => source_default.yellow(content),
73077
+ else: () => content
73078
+ }));
73079
+ const boldHandled = headingHandled.replace(/\*\*(.*?)\*\*/g, (_3, content) => source_default.bold(content));
73080
+ return boldHandled;
73023
73081
  }
73024
73082
  async function analyzeInput(input, openAIKey) {
73025
- const spinner = ora("Analyzing content...").start();
73083
+ const spinner = ora("Thinking...").start();
73026
73084
  try {
73027
73085
  const result = await clai(input, openAIKey);
73028
- spinner.succeed("Analysis complete");
73086
+ spinner.succeed("AHA!");
73029
73087
  console.log(source_default.green.bold(`
73030
- \uD83D\uDCDD Summary:`));
73088
+ \uD83D\uDCDD \u2550\u2550\u2550 Summary \u2550\u2550\u2550 :`));
73031
73089
  const formattedContent = formatMarkdownForTerminal(result.summary);
73032
73090
  await animateText(formattedContent);
73033
73091
  const { selectedLink } = await esm_default12.prompt([
package/dist/index.js CHANGED
@@ -41042,8 +41042,6 @@ async function getGoogleResults(query) {
41042
41042
  const urlLower = url.toLowerCase();
41043
41043
  return !urlLower.includes("www.google") && !urlLower.includes("gstatic.com") && !urlLower.includes("googleapis.com") && !urlLower.includes("googleadservices") && queryWords.some((word) => urlLower.includes(word));
41044
41044
  }));
41045
- console.log("queryWords", queryWords);
41046
- console.log("filteredUrls", filteredUrls);
41047
41045
  const results = [...filteredUrls].slice(0, 3);
41048
41046
  if (results.length === 0) {
41049
41047
  throw new Error("No search results found");
@@ -46045,7 +46043,7 @@ function truncateContent(content) {
46045
46043
  const maxChars = MAX_INPUT_TOKENS * 4;
46046
46044
  if (content.length <= maxChars)
46047
46045
  return content;
46048
- return content.slice(0, maxChars) + "... (content truncated)";
46046
+ return content.slice(0, maxChars);
46049
46047
  }
46050
46048
 
46051
46049
  class OpenAIWrapper {
@@ -46067,8 +46065,8 @@ class OpenAIWrapper {
46067
46065
  async completeStructured(prompt, options) {
46068
46066
  const truncatedPrompt = truncateContent(prompt);
46069
46067
  const {
46070
- model = "gpt-3.5-turbo",
46071
- temperature = 0.6,
46068
+ model = "gpt-4o-mini",
46069
+ temperature = 1.6,
46072
46070
  functionName = "generate_response",
46073
46071
  responseSchema
46074
46072
  } = options;
@@ -46119,6 +46117,7 @@ async function summarizeWebPage(content, openAIApiKey) {
46119
46117
  - Step 2: Do that
46120
46118
  - Step 3: Done
46121
46119
  10. Mark proper nouns as bold e.g. **Harry Potter**
46120
+ 11. Mark headings (h1, h2, h3) as #, ##, ### respectively
46122
46121
 
46123
46122
  Don't just summarize, cite the key information.
46124
46123
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fettstorch/clai",
3
- "version": "0.1.4",
3
+ "version": "0.1.6",
4
4
  "main": "dist/index.js",
5
5
  "bin": {
6
6
  "clai": "dist/cli.js"
package/src/cli.ts CHANGED
@@ -4,6 +4,8 @@ import inquirer from 'inquirer';
4
4
  import chalk from 'chalk';
5
5
  import ora from 'ora';
6
6
  import { clai } from './index';
7
+ import { when } from '@fettstorch/jule';
8
+ import pkg from '../package.json' assert { type: 'json' };
7
9
 
8
10
  const program = new Command();
9
11
 
@@ -12,7 +14,7 @@ async function main() {
12
14
  program
13
15
  .name('clai')
14
16
  .description('AI-powered web scraping tool')
15
- .version('1.0.0')
17
+ .version(pkg.version)
16
18
  .argument('[input...]', 'URL or search terms to analyze')
17
19
  .action(async (inputs: string[]) => {
18
20
  const openAIKey = process.env.OPENAI_API_KEY;
@@ -88,17 +90,28 @@ async function animateText(text: string, delay = 25) {
88
90
  }
89
91
 
90
92
  function formatMarkdownForTerminal(text: string): string {
91
- return text.replace(/\*\*(.*?)\*\*/g, (_, content) => chalk.bold(content));
93
+ // Handle headings first
94
+ const headingHandled = text.replace(/^(#{1,3})\s+(.*?)$/gm, (_, hashes, content) => when(hashes.length)({
95
+ 1: () => `\n${chalk.yellow.bold('═══ ')}${chalk.yellow.bold(content)}${chalk.yellow.bold(' ═══')}`,
96
+ 2: () => chalk.yellowBright.bold(content),
97
+ 3: () => chalk.yellow(content),
98
+ else: () => content
99
+ }));
100
+
101
+ // Handle regular bold text after headings
102
+ const boldHandled = headingHandled.replace(/\*\*(.*?)\*\*/g, (_, content) => chalk.bold(content));
103
+
104
+ return boldHandled;
92
105
  }
93
106
 
94
107
  async function analyzeInput(input: string, openAIKey: string) {
95
- const spinner = ora('Analyzing content...').start();
108
+ const spinner = ora('Thinking...').start();
96
109
 
97
110
  try {
98
111
  const result = await clai(input, openAIKey);
99
- spinner.succeed('Analysis complete');
112
+ spinner.succeed('AHA!');
100
113
 
101
- console.log(chalk.green.bold('\n📝 Summary:'));
114
+ console.log(chalk.green.bold('\n📝 ═══ Summary ═══ :'));
102
115
  const formattedContent = formatMarkdownForTerminal(result.summary);
103
116
  await animateText(formattedContent);
104
117
 
package/src/scraper.ts CHANGED
@@ -79,9 +79,6 @@ async function getGoogleResults(query: string): Promise<string[]> {
79
79
  })
80
80
  );
81
81
 
82
- console.log('queryWords', queryWords);
83
- console.log('filteredUrls', filteredUrls);
84
-
85
82
  const results = [...filteredUrls].slice(0, 3);
86
83
 
87
84
  if (results.length === 0) {
package/src/summarizer.ts CHANGED
@@ -41,6 +41,7 @@ export async function summarizeWebPage(content: string, openAIApiKey: string): P
41
41
  - Step 2: Do that
42
42
  - Step 3: Done
43
43
  10. Mark proper nouns as bold e.g. **Harry Potter**
44
+ 11. Mark headings (h1, h2, h3) as #, ##, ### respectively
44
45
 
45
46
  Don't just summarize, cite the key information.
46
47
 
package/tsconfig.json CHANGED
@@ -8,7 +8,8 @@
8
8
  "rootDir": "./src",
9
9
  "strict": true,
10
10
  "skipLibCheck": true,
11
- "forceConsistentCasingInFileNames": true
11
+ "forceConsistentCasingInFileNames": true,
12
+ "resolveJsonModule": true
12
13
  },
13
14
  "include": ["src/**/*"]
14
15
  }