@fyow/copilot-everything 1.0.0 → 1.0.1

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,390 @@
1
+ /**
2
+ * Package Manager Detection and Selection
3
+ * Automatically detects the preferred package manager or lets user choose
4
+ *
5
+ * Supports: npm, pnpm, yarn, bun
6
+ */
7
+
8
+ const fs = require('fs');
9
+ const path = require('path');
10
+ const { commandExists, getClaudeDir, readFile, writeFile, log, runCommand } = require('./utils');
11
+
12
+ // Package manager definitions
13
+ const PACKAGE_MANAGERS = {
14
+ npm: {
15
+ name: 'npm',
16
+ lockFile: 'package-lock.json',
17
+ installCmd: 'npm install',
18
+ runCmd: 'npm run',
19
+ execCmd: 'npx',
20
+ testCmd: 'npm test',
21
+ buildCmd: 'npm run build',
22
+ devCmd: 'npm run dev'
23
+ },
24
+ pnpm: {
25
+ name: 'pnpm',
26
+ lockFile: 'pnpm-lock.yaml',
27
+ installCmd: 'pnpm install',
28
+ runCmd: 'pnpm',
29
+ execCmd: 'pnpm dlx',
30
+ testCmd: 'pnpm test',
31
+ buildCmd: 'pnpm build',
32
+ devCmd: 'pnpm dev'
33
+ },
34
+ yarn: {
35
+ name: 'yarn',
36
+ lockFile: 'yarn.lock',
37
+ installCmd: 'yarn',
38
+ runCmd: 'yarn',
39
+ execCmd: 'yarn dlx',
40
+ testCmd: 'yarn test',
41
+ buildCmd: 'yarn build',
42
+ devCmd: 'yarn dev'
43
+ },
44
+ bun: {
45
+ name: 'bun',
46
+ lockFile: 'bun.lockb',
47
+ installCmd: 'bun install',
48
+ runCmd: 'bun run',
49
+ execCmd: 'bunx',
50
+ testCmd: 'bun test',
51
+ buildCmd: 'bun run build',
52
+ devCmd: 'bun run dev'
53
+ }
54
+ };
55
+
56
+ // Priority order for detection
57
+ const DETECTION_PRIORITY = ['pnpm', 'bun', 'yarn', 'npm'];
58
+
59
+ // Config file path
60
+ function getConfigPath() {
61
+ return path.join(getClaudeDir(), 'package-manager.json');
62
+ }
63
+
64
+ /**
65
+ * Load saved package manager configuration
66
+ */
67
+ function loadConfig() {
68
+ const configPath = getConfigPath();
69
+ const content = readFile(configPath);
70
+
71
+ if (content) {
72
+ try {
73
+ return JSON.parse(content);
74
+ } catch {
75
+ return null;
76
+ }
77
+ }
78
+ return null;
79
+ }
80
+
81
+ /**
82
+ * Save package manager configuration
83
+ */
84
+ function saveConfig(config) {
85
+ const configPath = getConfigPath();
86
+ writeFile(configPath, JSON.stringify(config, null, 2));
87
+ }
88
+
89
+ /**
90
+ * Detect package manager from lock file in project directory
91
+ */
92
+ function detectFromLockFile(projectDir = process.cwd()) {
93
+ for (const pmName of DETECTION_PRIORITY) {
94
+ const pm = PACKAGE_MANAGERS[pmName];
95
+ const lockFilePath = path.join(projectDir, pm.lockFile);
96
+
97
+ if (fs.existsSync(lockFilePath)) {
98
+ return pmName;
99
+ }
100
+ }
101
+ return null;
102
+ }
103
+
104
+ /**
105
+ * Detect package manager from package.json packageManager field
106
+ */
107
+ function detectFromPackageJson(projectDir = process.cwd()) {
108
+ const packageJsonPath = path.join(projectDir, 'package.json');
109
+ const content = readFile(packageJsonPath);
110
+
111
+ if (content) {
112
+ try {
113
+ const pkg = JSON.parse(content);
114
+ if (pkg.packageManager) {
115
+ // Format: "pnpm@8.6.0" or just "pnpm"
116
+ const pmName = pkg.packageManager.split('@')[0];
117
+ if (PACKAGE_MANAGERS[pmName]) {
118
+ return pmName;
119
+ }
120
+ }
121
+ } catch {
122
+ // Invalid package.json
123
+ }
124
+ }
125
+ return null;
126
+ }
127
+
128
+ /**
129
+ * Get available package managers (installed on system)
130
+ */
131
+ function getAvailablePackageManagers() {
132
+ const available = [];
133
+
134
+ for (const pmName of Object.keys(PACKAGE_MANAGERS)) {
135
+ if (commandExists(pmName)) {
136
+ available.push(pmName);
137
+ }
138
+ }
139
+
140
+ return available;
141
+ }
142
+
143
+ /**
144
+ * Get the package manager to use for current project
145
+ *
146
+ * Detection priority:
147
+ * 1. Environment variable CLAUDE_PACKAGE_MANAGER
148
+ * 2. Project-specific config (in .claude/package-manager.json)
149
+ * 3. package.json packageManager field
150
+ * 4. Lock file detection
151
+ * 5. Global user preference (in ~/.claude/package-manager.json)
152
+ * 6. First available package manager (by priority)
153
+ *
154
+ * @param {object} options - { projectDir, fallbackOrder }
155
+ * @returns {object} - { name, config, source }
156
+ */
157
+ function getPackageManager(options = {}) {
158
+ const { projectDir = process.cwd(), fallbackOrder = DETECTION_PRIORITY } = options;
159
+
160
+ // 1. Check environment variable
161
+ const envPm = process.env.CLAUDE_PACKAGE_MANAGER;
162
+ if (envPm && PACKAGE_MANAGERS[envPm]) {
163
+ return {
164
+ name: envPm,
165
+ config: PACKAGE_MANAGERS[envPm],
166
+ source: 'environment'
167
+ };
168
+ }
169
+
170
+ // 2. Check project-specific config
171
+ const projectConfigPath = path.join(projectDir, '.claude', 'package-manager.json');
172
+ const projectConfig = readFile(projectConfigPath);
173
+ if (projectConfig) {
174
+ try {
175
+ const config = JSON.parse(projectConfig);
176
+ if (config.packageManager && PACKAGE_MANAGERS[config.packageManager]) {
177
+ return {
178
+ name: config.packageManager,
179
+ config: PACKAGE_MANAGERS[config.packageManager],
180
+ source: 'project-config'
181
+ };
182
+ }
183
+ } catch {
184
+ // Invalid config
185
+ }
186
+ }
187
+
188
+ // 3. Check package.json packageManager field
189
+ const fromPackageJson = detectFromPackageJson(projectDir);
190
+ if (fromPackageJson) {
191
+ return {
192
+ name: fromPackageJson,
193
+ config: PACKAGE_MANAGERS[fromPackageJson],
194
+ source: 'package.json'
195
+ };
196
+ }
197
+
198
+ // 4. Check lock file
199
+ const fromLockFile = detectFromLockFile(projectDir);
200
+ if (fromLockFile) {
201
+ return {
202
+ name: fromLockFile,
203
+ config: PACKAGE_MANAGERS[fromLockFile],
204
+ source: 'lock-file'
205
+ };
206
+ }
207
+
208
+ // 5. Check global user preference
209
+ const globalConfig = loadConfig();
210
+ if (globalConfig && globalConfig.packageManager && PACKAGE_MANAGERS[globalConfig.packageManager]) {
211
+ return {
212
+ name: globalConfig.packageManager,
213
+ config: PACKAGE_MANAGERS[globalConfig.packageManager],
214
+ source: 'global-config'
215
+ };
216
+ }
217
+
218
+ // 6. Use first available package manager
219
+ const available = getAvailablePackageManagers();
220
+ for (const pmName of fallbackOrder) {
221
+ if (available.includes(pmName)) {
222
+ return {
223
+ name: pmName,
224
+ config: PACKAGE_MANAGERS[pmName],
225
+ source: 'fallback'
226
+ };
227
+ }
228
+ }
229
+
230
+ // Default to npm (always available with Node.js)
231
+ return {
232
+ name: 'npm',
233
+ config: PACKAGE_MANAGERS.npm,
234
+ source: 'default'
235
+ };
236
+ }
237
+
238
+ /**
239
+ * Set user's preferred package manager (global)
240
+ */
241
+ function setPreferredPackageManager(pmName) {
242
+ if (!PACKAGE_MANAGERS[pmName]) {
243
+ throw new Error(`Unknown package manager: ${pmName}`);
244
+ }
245
+
246
+ const config = loadConfig() || {};
247
+ config.packageManager = pmName;
248
+ config.setAt = new Date().toISOString();
249
+ saveConfig(config);
250
+
251
+ return config;
252
+ }
253
+
254
+ /**
255
+ * Set project's preferred package manager
256
+ */
257
+ function setProjectPackageManager(pmName, projectDir = process.cwd()) {
258
+ if (!PACKAGE_MANAGERS[pmName]) {
259
+ throw new Error(`Unknown package manager: ${pmName}`);
260
+ }
261
+
262
+ const configDir = path.join(projectDir, '.claude');
263
+ const configPath = path.join(configDir, 'package-manager.json');
264
+
265
+ const config = {
266
+ packageManager: pmName,
267
+ setAt: new Date().toISOString()
268
+ };
269
+
270
+ writeFile(configPath, JSON.stringify(config, null, 2));
271
+ return config;
272
+ }
273
+
274
+ /**
275
+ * Get the command to run a script
276
+ * @param {string} script - Script name (e.g., "dev", "build", "test")
277
+ * @param {object} options - { projectDir }
278
+ */
279
+ function getRunCommand(script, options = {}) {
280
+ const pm = getPackageManager(options);
281
+
282
+ switch (script) {
283
+ case 'install':
284
+ return pm.config.installCmd;
285
+ case 'test':
286
+ return pm.config.testCmd;
287
+ case 'build':
288
+ return pm.config.buildCmd;
289
+ case 'dev':
290
+ return pm.config.devCmd;
291
+ default:
292
+ return `${pm.config.runCmd} ${script}`;
293
+ }
294
+ }
295
+
296
+ /**
297
+ * Get the command to execute a package binary
298
+ * @param {string} binary - Binary name (e.g., "prettier", "eslint")
299
+ * @param {string} args - Arguments to pass
300
+ */
301
+ function getExecCommand(binary, args = '', options = {}) {
302
+ const pm = getPackageManager(options);
303
+ return `${pm.config.execCmd} ${binary}${args ? ' ' + args : ''}`;
304
+ }
305
+
306
+ /**
307
+ * Interactive prompt for package manager selection
308
+ * Returns a message for Claude to show to user
309
+ */
310
+ function getSelectionPrompt() {
311
+ const available = getAvailablePackageManagers();
312
+ const current = getPackageManager();
313
+
314
+ let message = '[PackageManager] Available package managers:\n';
315
+
316
+ for (const pmName of available) {
317
+ const indicator = pmName === current.name ? ' (current)' : '';
318
+ message += ` - ${pmName}${indicator}\n`;
319
+ }
320
+
321
+ message += '\nTo set your preferred package manager:\n';
322
+ message += ' - Global: Set CLAUDE_PACKAGE_MANAGER environment variable\n';
323
+ message += ' - Or add to ~/.claude/package-manager.json: {"packageManager": "pnpm"}\n';
324
+ message += ' - Or add to package.json: {"packageManager": "pnpm@8"}\n';
325
+
326
+ return message;
327
+ }
328
+
329
+ /**
330
+ * Generate a regex pattern that matches commands for all package managers
331
+ * @param {string} action - Action pattern (e.g., "run dev", "install", "test")
332
+ */
333
+ function getCommandPattern(action) {
334
+ const patterns = [];
335
+
336
+ if (action === 'dev') {
337
+ patterns.push(
338
+ 'npm run dev',
339
+ 'pnpm( run)? dev',
340
+ 'yarn dev',
341
+ 'bun run dev'
342
+ );
343
+ } else if (action === 'install') {
344
+ patterns.push(
345
+ 'npm install',
346
+ 'pnpm install',
347
+ 'yarn( install)?',
348
+ 'bun install'
349
+ );
350
+ } else if (action === 'test') {
351
+ patterns.push(
352
+ 'npm test',
353
+ 'pnpm test',
354
+ 'yarn test',
355
+ 'bun test'
356
+ );
357
+ } else if (action === 'build') {
358
+ patterns.push(
359
+ 'npm run build',
360
+ 'pnpm( run)? build',
361
+ 'yarn build',
362
+ 'bun run build'
363
+ );
364
+ } else {
365
+ // Generic run command
366
+ patterns.push(
367
+ `npm run ${action}`,
368
+ `pnpm( run)? ${action}`,
369
+ `yarn ${action}`,
370
+ `bun run ${action}`
371
+ );
372
+ }
373
+
374
+ return `(${patterns.join('|')})`;
375
+ }
376
+
377
+ module.exports = {
378
+ PACKAGE_MANAGERS,
379
+ DETECTION_PRIORITY,
380
+ getPackageManager,
381
+ setPreferredPackageManager,
382
+ setProjectPackageManager,
383
+ getAvailablePackageManagers,
384
+ detectFromLockFile,
385
+ detectFromPackageJson,
386
+ getRunCommand,
387
+ getExecCommand,
388
+ getSelectionPrompt,
389
+ getCommandPattern
390
+ };