prjct-cli 0.10.6 → 0.10.8

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.
@@ -0,0 +1,78 @@
1
+ ---
2
+ allowed-tools: [Read, Glob, Bash]
3
+ description: 'Analyze project technology stack - Claude reads and decides'
4
+ ---
5
+
6
+ # Project Analysis Instructions
7
+
8
+ ## Objective
9
+
10
+ Determine the technology stack by READING actual files, not assuming.
11
+
12
+ ## Step 1: Read Dependency Files
13
+
14
+ Read these files if they exist:
15
+
16
+ - `package.json` → Node.js/JavaScript/TypeScript project
17
+ - `Cargo.toml` → Rust project
18
+ - `go.mod` → Go project
19
+ - `requirements.txt` or `pyproject.toml` → Python project
20
+ - `Gemfile` → Ruby project
21
+ - `mix.exs` → Elixir project
22
+ - `pom.xml` or `build.gradle` → Java project
23
+ - `composer.json` → PHP project
24
+
25
+ **Extract actual dependencies** - don't guess frameworks.
26
+
27
+ ## Step 2: Examine Directory Structure
28
+
29
+ List the root directory to identify:
30
+
31
+ - Source directories (src/, lib/, app/, cmd/)
32
+ - Test directories (tests/, spec/, __tests__/)
33
+ - Config directories (.github/, .gitlab/, .vscode/)
34
+ - Build outputs (dist/, build/, target/)
35
+
36
+ ## Step 3: Check Configuration Files
37
+
38
+ Look for:
39
+
40
+ - `tsconfig.json` → TypeScript configuration
41
+ - `Dockerfile` → Docker usage
42
+ - `docker-compose.yml` → Multi-container setup
43
+ - `.eslintrc`, `.prettierrc` → Linting/formatting
44
+ - `jest.config.js`, `vitest.config.ts` → Testing setup
45
+ - `.env*` files → Environment configuration
46
+
47
+ ## Step 4: Determine Stack
48
+
49
+ Based on what you READ (not assumed):
50
+
51
+ 1. **Languages**: Identify from file extensions and configs
52
+ 2. **Frameworks**: Extract from actual dependencies
53
+ 3. **Tools**: Identify from config files present
54
+ 4. **Databases**: Look for DB clients in dependencies
55
+ 5. **Testing**: Identify test frameworks from configs/deps
56
+
57
+ ## Output Format
58
+
59
+ Provide analysis in this structure:
60
+
61
+ ```json
62
+ {
63
+ "languages": ["actual languages found"],
64
+ "frameworks": ["actual frameworks from deps"],
65
+ "tools": ["tools with config files present"],
66
+ "databases": ["database clients in deps"],
67
+ "testing": ["test frameworks found"],
68
+ "notes": "any observations about the project"
69
+ }
70
+ ```
71
+
72
+ ## Rules
73
+
74
+ - **NO hardcoded assumptions** - React doesn't mean "frontend"
75
+ - **READ before deciding** - Don't guess based on filenames
76
+ - **Any stack works** - Elixir, Rust, Go, Python, etc.
77
+ - **Be specific** - Include versions when available
78
+ - **Note uncertainty** - If unclear, say so
@@ -1,365 +0,0 @@
1
- /**
2
- * TechDetector - Dynamic Technology Detection
3
- *
4
- * NO HARDCODING - Detects technologies from actual project files
5
- * Works with ANY technology stack (Elixir, Phoenix, Svelte, etc.)
6
- *
7
- * @version 1.0.0
8
- */
9
-
10
- const fs = require('fs').promises
11
- const path = require('path')
12
-
13
- class TechDetector {
14
- constructor(projectPath) {
15
- this.projectPath = projectPath
16
- this.cache = null
17
- }
18
-
19
- /**
20
- * Detect all technologies in the project
21
- * Returns structured data about languages, frameworks, tools
22
- * @returns {Promise<Object>} - { languages: [], frameworks: [], tools: [], packageManagers: [] }
23
- */
24
- async detectAll() {
25
- if (this.cache) {
26
- return this.cache
27
- }
28
-
29
- const detected = {
30
- languages: [],
31
- frameworks: [],
32
- tools: [],
33
- packageManagers: [],
34
- databases: [],
35
- buildTools: [],
36
- testFrameworks: []
37
- }
38
-
39
- // Detect from package managers (most reliable source)
40
- await this.detectFromPackageJson(detected)
41
- await this.detectFromCargoToml(detected)
42
- await this.detectFromGoMod(detected)
43
- await this.detectFromRequirements(detected)
44
- await this.detectFromGemfile(detected)
45
- await this.detectFromMixExs(detected) // Elixir
46
- await this.detectFromPomXml(detected) // Maven/Java
47
- await this.detectFromComposerJson(detected) // PHP
48
-
49
- // Detect from config files
50
- await this.detectFromConfigFiles(detected)
51
-
52
- // Detect from directory structure
53
- await this.detectFromStructure(detected)
54
-
55
- // Cache result
56
- this.cache = detected
57
- return detected
58
- }
59
-
60
- /**
61
- * Detect from package.json (Node.js/JavaScript/TypeScript)
62
- */
63
- async detectFromPackageJson(detected) {
64
- try {
65
- const packagePath = path.join(this.projectPath, 'package.json')
66
- const content = await fs.readFile(packagePath, 'utf-8')
67
- const pkg = JSON.parse(content)
68
-
69
- detected.packageManagers.push('npm')
70
-
71
- // Language
72
- if (pkg.dependencies?.typescript || pkg.devDependencies?.typescript) {
73
- detected.languages.push('TypeScript')
74
- } else {
75
- detected.languages.push('JavaScript')
76
- }
77
-
78
- // Collect all dependencies (no hardcoding - just list them)
79
- const allDeps = {
80
- ...(pkg.dependencies || {}),
81
- ...(pkg.devDependencies || {})
82
- }
83
-
84
- // Frameworks and tools are in dependencies - let Claude decide what's important
85
- // We just collect the names, no assumptions
86
- const depNames = Object.keys(allDeps)
87
-
88
- // Common patterns (but not hardcoded - just helpers)
89
- for (const dep of depNames) {
90
- // Frontend frameworks
91
- if (['react', 'vue', 'angular', 'svelte'].includes(dep.toLowerCase())) {
92
- detected.frameworks.push(dep)
93
- }
94
- // Meta-frameworks
95
- else if (['next', 'nuxt', 'sveltekit', 'remix'].includes(dep.toLowerCase())) {
96
- detected.frameworks.push(dep)
97
- }
98
- // Backend frameworks
99
- else if (['express', 'fastify', 'koa', 'hapi', 'nest'].includes(dep.toLowerCase())) {
100
- detected.frameworks.push(dep)
101
- }
102
- // Build tools
103
- else if (['vite', 'webpack', 'rollup', 'esbuild', 'parcel'].includes(dep.toLowerCase())) {
104
- detected.buildTools.push(dep)
105
- }
106
- // Test frameworks
107
- else if (['jest', 'vitest', 'mocha', 'jasmine', 'cypress', 'playwright'].includes(dep.toLowerCase())) {
108
- detected.testFrameworks.push(dep)
109
- }
110
- // Databases
111
- else if (['mongoose', 'sequelize', 'prisma', 'typeorm'].includes(dep.toLowerCase())) {
112
- detected.databases.push(dep)
113
- }
114
- }
115
-
116
- // Also check for yarn/pnpm
117
- if (await this.fileExists('yarn.lock')) {
118
- detected.packageManagers.push('yarn')
119
- }
120
- if (await this.fileExists('pnpm-lock.yaml')) {
121
- detected.packageManagers.push('pnpm')
122
- }
123
- } catch (error) {
124
- // File doesn't exist or invalid JSON - skip
125
- }
126
- }
127
-
128
- /**
129
- * Detect from Cargo.toml (Rust)
130
- */
131
- async detectFromCargoToml(detected) {
132
- try {
133
- const cargoPath = path.join(this.projectPath, 'Cargo.toml')
134
- await fs.readFile(cargoPath, 'utf-8')
135
-
136
- detected.languages.push('Rust')
137
- detected.packageManagers.push('Cargo')
138
- } catch (error) {
139
- // File doesn't exist - skip
140
- }
141
- }
142
-
143
- /**
144
- * Detect from go.mod (Go)
145
- */
146
- async detectFromGoMod(detected) {
147
- try {
148
- const goModPath = path.join(this.projectPath, 'go.mod')
149
- await fs.readFile(goModPath, 'utf-8')
150
-
151
- detected.languages.push('Go')
152
- detected.packageManagers.push('Go Modules')
153
- } catch (error) {
154
- // File doesn't exist - skip
155
- }
156
- }
157
-
158
- /**
159
- * Detect from requirements.txt (Python)
160
- */
161
- async detectFromRequirements(detected) {
162
- try {
163
- const reqPath = path.join(this.projectPath, 'requirements.txt')
164
- const content = await fs.readFile(reqPath, 'utf-8')
165
-
166
- detected.languages.push('Python')
167
- detected.packageManagers.push('pip')
168
-
169
- // Detect common frameworks
170
- const lines = content.split('\n').map(l => l.trim().toLowerCase())
171
- if (lines.some(l => l.includes('django'))) detected.frameworks.push('Django')
172
- if (lines.some(l => l.includes('flask'))) detected.frameworks.push('Flask')
173
- if (lines.some(l => l.includes('fastapi'))) detected.frameworks.push('FastAPI')
174
- } catch (error) {
175
- // File doesn't exist - skip
176
- }
177
- }
178
-
179
- /**
180
- * Detect from Gemfile (Ruby)
181
- */
182
- async detectFromGemfile(detected) {
183
- try {
184
- const gemfilePath = path.join(this.projectPath, 'Gemfile')
185
- const content = await fs.readFile(gemfilePath, 'utf-8')
186
-
187
- detected.languages.push('Ruby')
188
- detected.packageManagers.push('Bundler')
189
-
190
- if (content.includes('rails')) {
191
- detected.frameworks.push('Rails')
192
- }
193
- } catch (error) {
194
- // File doesn't exist - skip
195
- }
196
- }
197
-
198
- /**
199
- * Detect from mix.exs (Elixir)
200
- */
201
- async detectFromMixExs(detected) {
202
- try {
203
- const mixPath = path.join(this.projectPath, 'mix.exs')
204
- const content = await fs.readFile(mixPath, 'utf-8')
205
-
206
- detected.languages.push('Elixir')
207
- detected.packageManagers.push('Mix')
208
-
209
- if (content.includes('phoenix')) {
210
- detected.frameworks.push('Phoenix')
211
- }
212
- } catch (error) {
213
- // File doesn't exist - skip
214
- }
215
- }
216
-
217
- /**
218
- * Detect from pom.xml (Maven/Java)
219
- */
220
- async detectFromPomXml(detected) {
221
- try {
222
- const pomPath = path.join(this.projectPath, 'pom.xml')
223
- await fs.readFile(pomPath, 'utf-8')
224
-
225
- detected.languages.push('Java')
226
- detected.packageManagers.push('Maven')
227
- } catch (error) {
228
- // File doesn't exist - skip
229
- }
230
- }
231
-
232
- /**
233
- * Detect from composer.json (PHP)
234
- */
235
- async detectFromComposerJson(detected) {
236
- try {
237
- const composerPath = path.join(this.projectPath, 'composer.json')
238
- const content = await fs.readFile(composerPath, 'utf-8')
239
- const composer = JSON.parse(content)
240
-
241
- detected.languages.push('PHP')
242
- detected.packageManagers.push('Composer')
243
-
244
- const allDeps = {
245
- ...(composer.require || {}),
246
- ...(composer['require-dev'] || {})
247
- }
248
-
249
- if (Object.keys(allDeps).some(d => d.includes('laravel'))) {
250
- detected.frameworks.push('Laravel')
251
- }
252
- if (Object.keys(allDeps).some(d => d.includes('symfony'))) {
253
- detected.frameworks.push('Symfony')
254
- }
255
- } catch (error) {
256
- // File doesn't exist - skip
257
- }
258
- }
259
-
260
- /**
261
- * Detect from config files (Docker, etc.)
262
- */
263
- async detectFromConfigFiles(detected) {
264
- // Docker
265
- if (await this.fileExists('Dockerfile')) {
266
- detected.tools.push('Docker')
267
- }
268
- if (await this.fileExists('docker-compose.yml') || await this.fileExists('docker-compose.yaml')) {
269
- detected.tools.push('Docker Compose')
270
- }
271
-
272
- // Kubernetes
273
- if (await this.fileExists('k8s') || await this.fileExists('kubernetes')) {
274
- detected.tools.push('Kubernetes')
275
- }
276
-
277
- // Terraform
278
- if (await this.fileExists('.tf') || await this.findFiles('*.tf')) {
279
- detected.tools.push('Terraform')
280
- }
281
- }
282
-
283
- /**
284
- * Detect from directory structure
285
- */
286
- async detectFromStructure(_detected) {
287
- try {
288
- const entries = await fs.readdir(this.projectPath, { withFileTypes: true })
289
- const dirs = entries.filter(e => e.isDirectory()).map(e => e.name)
290
-
291
- // Common patterns (but not assumptions - just hints)
292
- // Could analyze structure here in the future
293
- if (dirs.includes('src') && dirs.includes('lib')) {
294
- // Could be Elixir, but don't assume
295
- }
296
- } catch (error) {
297
- // Can't read directory - skip
298
- }
299
- }
300
-
301
- /**
302
- * Get a summary string of detected technologies
303
- * Useful for agent generation
304
- */
305
- async getSummary() {
306
- const tech = await this.detectAll()
307
- const parts = []
308
-
309
- if (tech.languages.length > 0) {
310
- parts.push(`Languages: ${tech.languages.join(', ')}`)
311
- }
312
- if (tech.frameworks.length > 0) {
313
- parts.push(`Frameworks: ${tech.frameworks.join(', ')}`)
314
- }
315
- if (tech.tools.length > 0) {
316
- parts.push(`Tools: ${tech.tools.join(', ')}`)
317
- }
318
- if (tech.databases.length > 0) {
319
- parts.push(`Databases: ${tech.databases.join(', ')}`)
320
- }
321
-
322
- return parts.join(' | ')
323
- }
324
-
325
- /**
326
- * Helper: Check if file exists
327
- */
328
- async fileExists(filename) {
329
- try {
330
- await fs.access(path.join(this.projectPath, filename))
331
- return true
332
- } catch {
333
- return false
334
- }
335
- }
336
-
337
- /**
338
- * Helper: Find files matching pattern
339
- */
340
- async findFiles(pattern) {
341
- try {
342
- const { exec } = require('child_process')
343
- const { promisify } = require('util')
344
- const execAsync = promisify(exec)
345
-
346
- const { stdout } = await execAsync(
347
- `find . -name "${pattern}" -type f ! -path "*/node_modules/*" ! -path "*/.git/*" | head -1`,
348
- { cwd: this.projectPath }
349
- )
350
- return stdout.trim().length > 0
351
- } catch {
352
- return false
353
- }
354
- }
355
-
356
- /**
357
- * Clear cache (useful after project changes)
358
- */
359
- clearCache() {
360
- this.cache = null
361
- }
362
- }
363
-
364
- module.exports = TechDetector
365
-