quackstack 1.0.1 → 1.0.3

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.
@@ -11,31 +11,35 @@ export async function ingest(rootDir, projectName, silent = false) {
11
11
  if (!silent)
12
12
  console.log(`Found ${files.length} files to process`);
13
13
  let processedCount = 0;
14
- for (const filePath of files) {
15
- try {
16
- const content = fs.readFileSync(filePath, "utf-8");
17
- const chunks = chunkCode(content, filePath);
18
- for (const chunk of chunks) {
19
- const embedding = await aiClient.getEmbeddings(chunk.content);
20
- await saveToDB({
21
- content: chunk.content,
22
- embedding,
23
- filePath,
24
- projectName,
25
- language: path.extname(filePath),
26
- functionName: chunk.functionName,
27
- lineStart: chunk.lineStart,
28
- lineEnd: chunk.lineEnd,
29
- });
14
+ const BATCH_SIZE = 10;
15
+ for (let i = 0; i < files.length; i += BATCH_SIZE) {
16
+ const batch = files.slice(i, i + BATCH_SIZE);
17
+ await Promise.all(batch.map(async (filePath) => {
18
+ try {
19
+ const content = fs.readFileSync(filePath, "utf-8");
20
+ const chunks = chunkCode(content, filePath);
21
+ for (const chunk of chunks) {
22
+ const embedding = await aiClient.getEmbeddings(chunk.content);
23
+ await saveToDB({
24
+ content: chunk.content,
25
+ embedding,
26
+ filePath,
27
+ projectName,
28
+ language: path.extname(filePath),
29
+ functionName: chunk.functionName,
30
+ lineStart: chunk.lineStart,
31
+ lineEnd: chunk.lineEnd,
32
+ });
33
+ }
34
+ processedCount++;
35
+ if (!silent && processedCount % 10 === 0) {
36
+ console.log(`Processed ${processedCount}/${files.length} files...`);
37
+ }
30
38
  }
31
- processedCount++;
32
- if (!silent && processedCount % 10 === 0) {
33
- console.log(`Processed ${processedCount}/${files.length} files...`);
39
+ catch (error) {
40
+ console.error(`Error processing ${filePath}:`, error);
34
41
  }
35
- }
36
- catch (error) {
37
- console.error(`Error processing ${filePath}:`, error);
38
- }
42
+ }));
39
43
  }
40
44
  if (!silent)
41
45
  console.log(`Done! Processed ${processedCount} files.`);
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
- import { client } from "../lib/database.js";
3
2
  import { aiClient } from "../lib/ai-provider.js";
3
+ import { client } from "../lib/database.js";
4
4
  function cosineSim(a, b) {
5
5
  let dot = 0, normA = 0, normB = 0;
6
6
  for (let i = 0; i < a.length; i++) {
@@ -15,16 +15,22 @@ export async function search(query, projectName) {
15
15
  const snippets = await client.codeSnippet.findMany({
16
16
  where: { projectName },
17
17
  });
18
- const ranked = snippets
19
- .map(snippet => ({
18
+ const scored = snippets.map(snippet => ({
20
19
  id: snippet.id,
21
20
  content: snippet.content,
22
21
  filePath: snippet.filePath,
23
22
  functionName: snippet.functionName,
24
23
  score: cosineSim(queryEmbedding, snippet.embedding),
25
- }))
26
- .sort((a, b) => b.score - a.score)
27
- .slice(0, 5);
24
+ }));
25
+ scored.sort((a, b) => b.score - a.score);
26
+ const seenFiles = new Set();
27
+ const ranked = scored.filter(item => {
28
+ if (seenFiles.has(item.filePath)) {
29
+ return false;
30
+ }
31
+ seenFiles.add(item.filePath);
32
+ return true;
33
+ }).slice(0, 5);
28
34
  const context = ranked
29
35
  .map((r, i) => `[${i + 1}] ${r.filePath}${r.functionName ? ` (${r.functionName})` : ""}\n${r.content}`)
30
36
  .join("\n\n---\n\n");
@@ -2,7 +2,7 @@ import OpenAI from "openai";
2
2
  import Anthropic from "@anthropic-ai/sdk";
3
3
  import { GoogleGenerativeAI } from "@google/generative-ai";
4
4
  import dotenv from "dotenv";
5
- dotenv.config();
5
+ dotenv.config({ quiet: true });
6
6
  export class AIClient {
7
7
  provider;
8
8
  openai;
package/dist/repl.js CHANGED
@@ -25,23 +25,24 @@ export async function startREPL(forceReindex = false) {
25
25
  const rl = readline.createInterface({
26
26
  input: process.stdin,
27
27
  output: process.stdout,
28
- prompt: "🐄 Quack! How can I help? > "
28
+ terminal: true
29
29
  });
30
30
  console.log("šŸ’” Tip: Press Ctrl+C to exit\n");
31
- rl.prompt();
32
- rl.on("line", async (input) => {
33
- const trimmed = input.trim();
34
- if (!trimmed) {
35
- rl.prompt();
36
- return;
37
- }
38
- try {
39
- const { answer, sources } = await search(trimmed, PROJECT_NAME);
40
- console.log(`\n${answer}\n`);
41
- await new Promise((resolve) => {
31
+ const askQuestion = () => {
32
+ rl.question("🐄 Quack! How can I help? > ", async (input) => {
33
+ const trimmed = input.trim();
34
+ if (!trimmed) {
35
+ askQuestion();
36
+ return;
37
+ }
38
+ try {
39
+ rl.pause();
40
+ const { answer, sources } = await search(trimmed, PROJECT_NAME);
41
+ console.log(`\n${answer}\n`);
42
42
  const detailRL = readline.createInterface({
43
43
  input: process.stdin,
44
- output: process.stdout
44
+ output: process.stdout,
45
+ terminal: true
45
46
  });
46
47
  detailRL.question("šŸ’” Want more details? (y/n) > ", (ans) => {
47
48
  if (ans.toLowerCase() === "y") {
@@ -54,15 +55,18 @@ export async function startREPL(forceReindex = false) {
54
55
  }
55
56
  detailRL.close();
56
57
  console.log();
57
- resolve();
58
+ rl.resume();
59
+ askQuestion();
58
60
  });
59
- });
60
- }
61
- catch (error) {
62
- console.error("āŒ Error:", error instanceof Error ? error.message : "Unknown error");
63
- }
64
- rl.prompt();
65
- });
61
+ }
62
+ catch (error) {
63
+ console.error("āŒ Error:", error instanceof Error ? error.message : "Unknown error");
64
+ rl.resume();
65
+ askQuestion();
66
+ }
67
+ });
68
+ };
69
+ askQuestion();
66
70
  rl.on("close", () => {
67
71
  console.log("\nšŸ‘‹ Happy coding!");
68
72
  process.exit(0);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "quackstack",
3
- "version": "1.0.1",
3
+ "version": "1.0.3",
4
4
  "description": "Your cracked unpaid intern for all things codebase related! AI-powered codebase search and Q&A.",
5
5
  "type": "module",
6
6
  "main": "dist/cli.cjs",
@@ -10,17 +10,20 @@
10
10
  "files": [
11
11
  "dist",
12
12
  "prisma",
13
+ "node_modules/.prisma",
14
+ "node_modules/@prisma/client",
13
15
  "README.md",
14
- "LICENSE"
16
+ "LICENSE",
17
+ ".npmrc"
15
18
  ],
16
19
  "publishConfig": {
17
20
  "access": "public"
18
21
  },
19
22
  "scripts": {
20
- "build": "tsc -b",
23
+ "build": "prisma generate && tsc -b",
21
24
  "dev": "tsc -b && node dist/cli.cjs",
22
25
  "prepare": "prisma generate",
23
- "postinstall": "prisma generate"
26
+ "prepack": "prisma generate"
24
27
  },
25
28
  "keywords": [
26
29
  "cli",
@@ -36,13 +39,22 @@
36
39
  "claude",
37
40
  "gemini",
38
41
  "deepseek",
39
- "mistral"
42
+ "mistral",
43
+ "developer-tools",
44
+ "productivity"
40
45
  ],
41
46
  "repository": {
42
47
  "type": "git",
43
48
  "url": "https://github.com/woustachemax/quackstack.git"
44
49
  },
45
- "author": "woustachemax",
50
+ "homepage": "https://github.com/woustachemax/quackstack#readme",
51
+ "bugs": {
52
+ "url": "https://github.com/woustachemax/quackstack/issues"
53
+ },
54
+ "author": {
55
+ "name": "woustachemax",
56
+ "url": "https://github.com/woustachemax"
57
+ },
46
58
  "license": "MIT",
47
59
  "engines": {
48
60
  "node": ">=20.0.0"