quackstack 1.0.21 → 1.0.23
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 +6 -0
- package/dist/commands/agents.js +265 -0
- package/package.json +1 -1
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(), "AGENTS.md");
|
|
20
|
+
const exists = await fs.access(agentPath).then(() => true).catch(() => false);
|
|
21
|
+
if (exists) {
|
|
22
|
+
const shouldOverwrite = await askQuestion("⚠️ AGENTS.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 AGENTS.md files following the agents.md specification (https://agents.md/).
|
|
67
|
+
|
|
68
|
+
Based on the codebase context provided, generate a comprehensive AGENTS.md file that describes this project as an AI agent.
|
|
69
|
+
|
|
70
|
+
The AGENTS.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 AGENTS.md...");
|
|
122
|
+
const agentMd = await aiClient.generateAnswer(prompt, context);
|
|
123
|
+
await fs.writeFile(agentPath, agentMd, "utf-8");
|
|
124
|
+
console.log(`AGENTS.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
|
+
}
|