quackstack 1.0.11 → 1.0.12
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 +3 -1
- package/dist/cli.cjs +6 -0
- package/dist/commands/readme.js +101 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
|
|
5
5
|
QuackStack is an interactive CLI tool that indexes your codebase using local AI embeddings and lets you ask questions about it conversationally. Perfect for understanding unfamiliar code, onboarding to new projects, or giving your AI coding assistant persistent context.
|
|
6
6
|
|
|
7
|
+
## 🎯 Quack in Action!
|
|
8
|
+
Check out the QuackStack Live demo [here](https://courageous-spaniel.clueso.site/share/4f5e6395-8ad8-4d18-8e81-f736a6581a25)!
|
|
7
9
|
## ✨ Features
|
|
8
10
|
|
|
9
11
|
* 🚀 **Zero-config** - Just run `quack` in any project directory
|
|
@@ -272,4 +274,4 @@ MIT
|
|
|
272
274
|
|
|
273
275
|
**Large Codebases**: First index might take a few minutes. After that, only changed files are re-indexed.
|
|
274
276
|
|
|
275
|
-
**No Vendor Lock-in**: Unlike other tools, QuackStack works with Cursor, Windsurf, Cline, Continue, and Aider - choose your favorite!
|
|
277
|
+
**No Vendor Lock-in**: Unlike other tools, QuackStack works with Cursor, Windsurf, Cline, Continue, and Aider - choose your favorite!
|
package/dist/cli.cjs
CHANGED
|
@@ -8,6 +8,7 @@ const commander_1 = require("commander");
|
|
|
8
8
|
const chalk_animation_1 = __importDefault(require("chalk-animation"));
|
|
9
9
|
const repl_js_1 = require("./repl.js");
|
|
10
10
|
const context_generator_js_1 = require("./lib/context-generator.js");
|
|
11
|
+
const readme_js_1 = require("./commands/readme.js");
|
|
11
12
|
const path_1 = __importDefault(require("path"));
|
|
12
13
|
const program = new commander_1.Command();
|
|
13
14
|
const PROJECT_NAME = path_1.default.basename(process.cwd());
|
|
@@ -18,12 +19,17 @@ program
|
|
|
18
19
|
.option("-r, --reindex", "Force reindex the codebase")
|
|
19
20
|
.option("-c, --context", "Generate context files for ALL AI coding tools (Cursor, Windsurf, Cline, Continue, Aider)")
|
|
20
21
|
.option("-d, --docs", "Generate CODEBASE.md - universal documentation for any IDE/editor")
|
|
22
|
+
.option("--readme", "Generate README.md from your codebase")
|
|
21
23
|
.option("--cursor", "[DEPRECATED] Use --context instead. Generates .cursorrules only")
|
|
22
24
|
.option("-w, --watch", "Watch mode: auto-update context files on file changes")
|
|
23
25
|
.action(async (options) => {
|
|
24
26
|
const title = chalk_animation_1.default.rainbow("Welcome to QuackStack! 🐥\n");
|
|
25
27
|
await new Promise(res => setTimeout(res, 1500));
|
|
26
28
|
title.stop();
|
|
29
|
+
if (options.readme) {
|
|
30
|
+
await (0, readme_js_1.generateReadme)(PROJECT_NAME);
|
|
31
|
+
process.exit(0);
|
|
32
|
+
}
|
|
27
33
|
if (options.context) {
|
|
28
34
|
await (0, context_generator_js_1.generateContextFiles)(PROJECT_NAME);
|
|
29
35
|
await (0, context_generator_js_1.updateGlobalContext)(PROJECT_NAME);
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { client } from "../lib/database.js";
|
|
2
|
+
import { getAIClient } from "../lib/ai-provider.js";
|
|
3
|
+
import fs from "fs/promises";
|
|
4
|
+
import path from "path";
|
|
5
|
+
import readline from "readline";
|
|
6
|
+
async function askQuestion(query) {
|
|
7
|
+
const rl = readline.createInterface({
|
|
8
|
+
input: process.stdin,
|
|
9
|
+
output: process.stdout,
|
|
10
|
+
});
|
|
11
|
+
return new Promise(resolve => {
|
|
12
|
+
rl.question(query, answer => {
|
|
13
|
+
rl.close();
|
|
14
|
+
resolve(answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes');
|
|
15
|
+
});
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
export async function generateReadme(projectName, outputPath) {
|
|
19
|
+
const readmePath = outputPath || path.join(process.cwd(), "README.md");
|
|
20
|
+
const exists = await fs.access(readmePath).then(() => true).catch(() => false);
|
|
21
|
+
if (exists) {
|
|
22
|
+
const shouldOverwrite = await askQuestion("⚠️ README.md already exists. Overwrite? (y/n): ");
|
|
23
|
+
if (!shouldOverwrite) {
|
|
24
|
+
console.log("Cancelled.");
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
const snippets = await client.codeSnippet.findMany({
|
|
29
|
+
where: { projectName },
|
|
30
|
+
});
|
|
31
|
+
const fileStructure = [...new Set(snippets.map(s => s.filePath))].sort();
|
|
32
|
+
const technologies = detectTechnologies(snippets);
|
|
33
|
+
const entryPoints = findEntryPoints(snippets);
|
|
34
|
+
const context = `
|
|
35
|
+
Project Structure:
|
|
36
|
+
${fileStructure.map(f => `- ${f}`).join('\n')}
|
|
37
|
+
|
|
38
|
+
Technologies Detected:
|
|
39
|
+
${technologies.join(', ')}
|
|
40
|
+
|
|
41
|
+
Entry Points:
|
|
42
|
+
${entryPoints.map(e => `- ${e.filePath} (${e.functionName})`).join('\n')}
|
|
43
|
+
|
|
44
|
+
Sample Code Snippets:
|
|
45
|
+
${snippets.slice(0, 10).map(s => `
|
|
46
|
+
File: ${s.filePath}
|
|
47
|
+
${s.content.slice(0, 200)}...
|
|
48
|
+
`).join('\n---\n')}
|
|
49
|
+
`;
|
|
50
|
+
const aiClient = getAIClient();
|
|
51
|
+
const prompt = `
|
|
52
|
+
You are a technical documentation expert. Based on the following codebase context, generate a comprehensive README.md file.
|
|
53
|
+
|
|
54
|
+
Include:
|
|
55
|
+
- Project title and description
|
|
56
|
+
- Technologies used
|
|
57
|
+
- Installation instructions
|
|
58
|
+
- Usage examples
|
|
59
|
+
- Project structure overview
|
|
60
|
+
- Key features
|
|
61
|
+
- Contributing guidelines (basic)
|
|
62
|
+
|
|
63
|
+
Make it professional, clear, and actionable.
|
|
64
|
+
|
|
65
|
+
Context:
|
|
66
|
+
${context}
|
|
67
|
+
`;
|
|
68
|
+
console.log("🦆 Generating README.md...");
|
|
69
|
+
const readme = await aiClient.generateAnswer(prompt, context);
|
|
70
|
+
await fs.writeFile(readmePath, readme, "utf-8");
|
|
71
|
+
console.log(`✅ README.md generated at ${readmePath}`);
|
|
72
|
+
}
|
|
73
|
+
function detectTechnologies(snippets) {
|
|
74
|
+
const techs = new Set();
|
|
75
|
+
snippets.forEach(s => {
|
|
76
|
+
const filePath = s.filePath.toLowerCase();
|
|
77
|
+
if (filePath.endsWith('.tsx') || filePath.endsWith('.jsx'))
|
|
78
|
+
techs.add('React');
|
|
79
|
+
if (filePath.endsWith('.ts'))
|
|
80
|
+
techs.add('TypeScript');
|
|
81
|
+
if (filePath.endsWith('.py'))
|
|
82
|
+
techs.add('Python');
|
|
83
|
+
if (filePath.endsWith('.rs'))
|
|
84
|
+
techs.add('Rust');
|
|
85
|
+
if (filePath.includes('prisma'))
|
|
86
|
+
techs.add('Prisma');
|
|
87
|
+
if (s.content.includes('express'))
|
|
88
|
+
techs.add('Express');
|
|
89
|
+
if (s.content.includes('fastapi'))
|
|
90
|
+
techs.add('FastAPI');
|
|
91
|
+
if (s.content.includes('django'))
|
|
92
|
+
techs.add('Django');
|
|
93
|
+
});
|
|
94
|
+
return Array.from(techs);
|
|
95
|
+
}
|
|
96
|
+
function findEntryPoints(snippets) {
|
|
97
|
+
return snippets.filter(s => s.filePath.includes('main') ||
|
|
98
|
+
s.filePath.includes('index') ||
|
|
99
|
+
s.filePath.includes('app') ||
|
|
100
|
+
s.functionName?.toLowerCase().includes('main')).slice(0, 5);
|
|
101
|
+
}
|