quackstack 1.0.20 → 1.0.22

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/dist/cli.cjs CHANGED
@@ -10,6 +10,7 @@ const chalk_1 = __importDefault(require("chalk"));
10
10
  const repl_js_1 = require("./repl.js");
11
11
  const context_generator_js_1 = require("./lib/context-generator.js");
12
12
  const readme_js_1 = require("./commands/readme.js");
13
+ const agents_js_1 = require("./commands/agents.js");
13
14
  const ai_provider_js_1 = require("./lib/ai-provider.js");
14
15
  const path_1 = __importDefault(require("path"));
15
16
  const program = new commander_1.Command();
@@ -22,6 +23,7 @@ program
22
23
  .option("-c, --context", "Generate context files for ALL AI coding tools")
23
24
  .option("-d, --docs", "Generate CODEBASE.md")
24
25
  .option("--readme", "Generate README.md from your codebase")
26
+ .option("--agent", "Generate agent.md configuration file")
25
27
  .option("--cursor", "[DEPRECATED] Use --context instead")
26
28
  .option("-w, --watch", "Watch mode: auto-update context files on file changes")
27
29
  .option("-p, --provider <provider>", "AI provider: openai, anthropic, gemini, deepseek, mistral, grok")
@@ -54,6 +56,10 @@ program
54
56
  const title = chalk_animation_1.default.rainbow("QuackStack\n");
55
57
  await new Promise(res => setTimeout(res, 1500));
56
58
  title.stop();
59
+ if (options.agent) {
60
+ await (0, agents_js_1.generateAgentMd)(PROJECT_NAME);
61
+ process.exit(0);
62
+ }
57
63
  if (options.readme) {
58
64
  await (0, readme_js_1.generateReadme)(PROJECT_NAME);
59
65
  process.exit(0);
@@ -0,0 +1,265 @@
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 generateAgentMd(projectName, outputPath) {
19
+ const agentPath = outputPath || path.join(process.cwd(), "agent.md");
20
+ const exists = await fs.access(agentPath).then(() => true).catch(() => false);
21
+ if (exists) {
22
+ const shouldOverwrite = await askQuestion("⚠️ agent.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 apiEndpoints = detectAPIEndpoints(snippets);
34
+ const dataModels = detectDataModels(snippets);
35
+ const toolsAndDeps = detectToolsAndDependencies(snippets);
36
+ const context = `
37
+ Project: ${projectName}
38
+
39
+ File Structure (${fileStructure.length} files):
40
+ ${fileStructure.slice(0, 50).map(f => `- ${f}`).join('\n')}
41
+ ${fileStructure.length > 50 ? `... and ${fileStructure.length - 50} more files` : ''}
42
+
43
+ Technologies & Frameworks:
44
+ ${technologies.join(', ')}
45
+
46
+ Tools & Dependencies:
47
+ ${toolsAndDeps.join(', ')}
48
+
49
+ API Endpoints:
50
+ ${apiEndpoints.slice(0, 10).map(e => `- ${e}`).join('\n')}
51
+
52
+ Data Models:
53
+ ${dataModels.slice(0, 10).map(m => `- ${m}`).join('\n')}
54
+
55
+ Key Code Patterns:
56
+ ${analyzeCodePatterns(snippets)}
57
+
58
+ Sample Implementation Details:
59
+ ${snippets.slice(0, 15).map(s => `
60
+ File: ${s.filePath}
61
+ ${s.content.slice(0, 300)}...
62
+ `).join('\n---\n')}
63
+ `;
64
+ const aiClient = getAIClient();
65
+ const prompt = `
66
+ You are an expert at creating agent.md files following the agents.md specification (https://agents.md/).
67
+
68
+ Based on the codebase context provided, generate a comprehensive agent.md file that describes this project as an AI agent.
69
+
70
+ The agent.md should include:
71
+
72
+ # Agent Metadata
73
+ - name: A descriptive name for this codebase agent
74
+ - description: What this agent/codebase does
75
+ - version: Current version
76
+ - author: Project author/team
77
+
78
+ # Capabilities
79
+ List what this agent can do based on the codebase:
80
+ - Key features and functionalities
81
+ - Available commands/operations
82
+ - API endpoints (if applicable)
83
+ - Data processing capabilities
84
+
85
+ # Technologies
86
+ - Programming languages
87
+ - Frameworks and libraries
88
+ - Database systems
89
+ - External services/APIs
90
+
91
+ # Configuration
92
+ - Environment variables needed
93
+ - Required API keys
94
+ - Setup requirements
95
+
96
+ # Usage Examples
97
+ - How to interact with this agent
98
+ - Common commands or API calls
99
+ - Example queries or operations
100
+
101
+ # Context & Knowledge
102
+ - What domain knowledge this agent has
103
+ - What it understands about the project structure
104
+ - Key architectural patterns
105
+
106
+ # Constraints & Limitations
107
+ - What the agent cannot do
108
+ - Known limitations
109
+ - Security considerations
110
+
111
+ # Tools & Integrations
112
+ - External tools used
113
+ - Available integrations
114
+ - CLI commands
115
+
116
+ Make it detailed, actionable, and follow the agents.md spec format with proper markdown sections.
117
+
118
+ Codebase Context:
119
+ ${context}
120
+ `;
121
+ console.log("Generating agent.md...");
122
+ const agentMd = await aiClient.generateAnswer(prompt, context);
123
+ await fs.writeFile(agentPath, agentMd, "utf-8");
124
+ console.log(`agent.md generated at ${agentPath}`);
125
+ }
126
+ function detectTechnologies(snippets) {
127
+ const techs = new Set();
128
+ snippets.forEach(s => {
129
+ const filePath = s.filePath.toLowerCase();
130
+ const content = s.content.toLowerCase();
131
+ if (filePath.endsWith('.tsx') || filePath.endsWith('.jsx'))
132
+ techs.add('React');
133
+ if (filePath.endsWith('.ts'))
134
+ techs.add('TypeScript');
135
+ if (filePath.endsWith('.js'))
136
+ techs.add('JavaScript');
137
+ if (filePath.endsWith('.py'))
138
+ techs.add('Python');
139
+ if (filePath.endsWith('.rs'))
140
+ techs.add('Rust');
141
+ if (filePath.endsWith('.go'))
142
+ techs.add('Go');
143
+ if (filePath.endsWith('.java'))
144
+ techs.add('Java');
145
+ if (filePath.includes('prisma') || content.includes('prisma'))
146
+ techs.add('Prisma');
147
+ if (content.includes('express'))
148
+ techs.add('Express');
149
+ if (content.includes('fastapi'))
150
+ techs.add('FastAPI');
151
+ if (content.includes('django'))
152
+ techs.add('Django');
153
+ if (content.includes('next'))
154
+ techs.add('Next.js');
155
+ if (content.includes('vue'))
156
+ techs.add('Vue');
157
+ if (content.includes('svelte'))
158
+ techs.add('Svelte');
159
+ if (content.includes('flask'))
160
+ techs.add('Flask');
161
+ if (content.includes('postgres') || content.includes('postgresql'))
162
+ techs.add('PostgreSQL');
163
+ if (content.includes('mongodb'))
164
+ techs.add('MongoDB');
165
+ if (content.includes('redis'))
166
+ techs.add('Redis');
167
+ if (content.includes('mysql'))
168
+ techs.add('MySQL');
169
+ if (content.includes('docker'))
170
+ techs.add('Docker');
171
+ if (content.includes('kubernetes'))
172
+ techs.add('Kubernetes');
173
+ if (content.includes('graphql'))
174
+ techs.add('GraphQL');
175
+ if (content.includes('grpc'))
176
+ techs.add('gRPC');
177
+ });
178
+ return Array.from(techs);
179
+ }
180
+ function detectAPIEndpoints(snippets) {
181
+ const endpoints = new Set();
182
+ snippets.forEach(s => {
183
+ const content = s.content;
184
+ const expressMatches = content.matchAll(/(?:app|router)\.(get|post|put|delete|patch)\(['"`]([^'"`]+)['"`]/g);
185
+ for (const match of expressMatches) {
186
+ endpoints.add(`${match[1].toUpperCase()} ${match[2]}`);
187
+ }
188
+ const pythonMatches = content.matchAll(/@(?:app|router|api)\.(get|post|put|delete|patch)\(['"`]([^'"`]+)['"`]/g);
189
+ for (const match of pythonMatches) {
190
+ endpoints.add(`${match[1].toUpperCase()} ${match[2]}`);
191
+ }
192
+ });
193
+ return Array.from(endpoints);
194
+ }
195
+ function detectDataModels(snippets) {
196
+ const models = new Set();
197
+ snippets.forEach(s => {
198
+ const content = s.content;
199
+ const prismaMatches = content.matchAll(/model\s+(\w+)\s*{/g);
200
+ for (const match of prismaMatches) {
201
+ models.add(`Prisma: ${match[1]}`);
202
+ }
203
+ const tsMatches = content.matchAll(/(?:interface|type)\s+(\w+)\s*[={]/g);
204
+ for (const match of tsMatches) {
205
+ if (match[1].length > 2 && match[1][0] === match[1][0].toUpperCase()) {
206
+ models.add(`Type: ${match[1]}`);
207
+ }
208
+ }
209
+ const pyMatches = content.matchAll(/class\s+(\w+)(?:\(.*?\))?:/g);
210
+ for (const match of pyMatches) {
211
+ models.add(`Class: ${match[1]}`);
212
+ }
213
+ });
214
+ return Array.from(models).slice(0, 20);
215
+ }
216
+ function detectToolsAndDependencies(snippets) {
217
+ const tools = new Set();
218
+ snippets.forEach(s => {
219
+ const content = s.content.toLowerCase();
220
+ if (content.includes('axios'))
221
+ tools.add('Axios');
222
+ if (content.includes('fetch'))
223
+ tools.add('Fetch API');
224
+ if (content.includes('chalk'))
225
+ tools.add('Chalk');
226
+ if (content.includes('commander'))
227
+ tools.add('Commander');
228
+ if (content.includes('readline'))
229
+ tools.add('Readline');
230
+ if (content.includes('openai'))
231
+ tools.add('OpenAI');
232
+ if (content.includes('anthropic'))
233
+ tools.add('Anthropic');
234
+ if (content.includes('langchain'))
235
+ tools.add('LangChain');
236
+ if (content.includes('pinecone'))
237
+ tools.add('Pinecone');
238
+ if (content.includes('supabase'))
239
+ tools.add('Supabase');
240
+ if (content.includes('stripe'))
241
+ tools.add('Stripe');
242
+ if (content.includes('aws'))
243
+ tools.add('AWS');
244
+ if (content.includes('vercel'))
245
+ tools.add('Vercel');
246
+ });
247
+ return Array.from(tools);
248
+ }
249
+ function analyzeCodePatterns(snippets) {
250
+ let patterns = [];
251
+ const hasAsync = snippets.some(s => s.content.includes('async'));
252
+ const hasPromises = snippets.some(s => s.content.includes('Promise'));
253
+ const hasClasses = snippets.some(s => /class\s+\w+/.test(s.content));
254
+ const hasFunctional = snippets.some(s => /const\s+\w+\s*=\s*\(.*?\)\s*=>/.test(s.content));
255
+ const hasErrorHandling = snippets.some(s => s.content.includes('try') && s.content.includes('catch'));
256
+ if (hasAsync || hasPromises)
257
+ patterns.push('Asynchronous operations');
258
+ if (hasClasses)
259
+ patterns.push('Object-oriented patterns');
260
+ if (hasFunctional)
261
+ patterns.push('Functional programming');
262
+ if (hasErrorHandling)
263
+ patterns.push('Error handling with try-catch');
264
+ return patterns.join(', ') || 'Standard procedural code';
265
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "quackstack",
3
- "version": "1.0.20",
3
+ "version": "1.0.22",
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",