@compilr-dev/sdk 0.2.4 → 0.2.6

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.
Files changed (30) hide show
  1. package/dist/index.d.ts +2 -0
  2. package/dist/index.js +12 -0
  3. package/dist/platform/sqlite/db.js +5 -3
  4. package/dist/project-generator/detection.d.ts +42 -0
  5. package/dist/project-generator/detection.js +401 -0
  6. package/dist/project-generator/generator.d.ts +14 -0
  7. package/dist/project-generator/generator.js +245 -0
  8. package/dist/project-generator/index.d.ts +11 -0
  9. package/dist/project-generator/index.js +13 -0
  10. package/dist/project-generator/templates/coding-standards.d.ts +7 -0
  11. package/dist/project-generator/templates/coding-standards.js +299 -0
  12. package/dist/project-generator/templates/compilr-md-import.d.ts +8 -0
  13. package/dist/project-generator/templates/compilr-md-import.js +241 -0
  14. package/dist/project-generator/templates/compilr-md.d.ts +7 -0
  15. package/dist/project-generator/templates/compilr-md.js +141 -0
  16. package/dist/project-generator/templates/config-json.d.ts +13 -0
  17. package/dist/project-generator/templates/config-json.js +39 -0
  18. package/dist/project-generator/templates/gitignore.d.ts +7 -0
  19. package/dist/project-generator/templates/gitignore.js +85 -0
  20. package/dist/project-generator/templates/index.d.ts +11 -0
  21. package/dist/project-generator/templates/index.js +11 -0
  22. package/dist/project-generator/templates/package-json.d.ts +7 -0
  23. package/dist/project-generator/templates/package-json.js +111 -0
  24. package/dist/project-generator/templates/readme-md.d.ts +7 -0
  25. package/dist/project-generator/templates/readme-md.js +165 -0
  26. package/dist/project-generator/templates/tsconfig.d.ts +7 -0
  27. package/dist/project-generator/templates/tsconfig.js +61 -0
  28. package/dist/project-generator/types.d.ts +95 -0
  29. package/dist/project-generator/types.js +24 -0
  30. package/package.json +1 -1
package/dist/index.d.ts CHANGED
@@ -65,5 +65,7 @@ export { DEFAULT_PERMISSION_RULES, findMatchingRule, permissionModeLabel, permis
65
65
  export type { PermissionRule, PermissionMode, PermissionLevel } from './permissions.js';
66
66
  export { readMCPConfigFile, writeMCPConfigFile, resolveServerEntry, loadMCPServers, saveMCPServerEntry, deleteMCPServerEntry, getServerNames, } from './mcp-config.js';
67
67
  export type { MCPServerEntry, MCPConfigFile, ResolvedMCPServer } from './mcp-config.js';
68
+ export { generateProject, isGitConfigured, generateCompilrMd, generateConfigJson, generateReadmeMd, generateCodingStandardsMd, generatePackageJson, generateTsconfig, generateGitignore, generateCompilrMdForImport, detectProjectInfo, detectGitInfo, prettifyName, getLanguageLabel, getFrameworkLabel, validateImportPath, isValidProjectName, projectExists, TECH_STACK_LABELS, CODING_STANDARDS_LABELS, REPO_PATTERN_LABELS, WORKFLOW_VERSION, } from './project-generator/index.js';
69
+ export type { TechStack, CodingStandards, GeneratorRepoPattern, ProjectConfig, GenerationResult, CompilrConfig, DetectedProject, GitInfo, ImportProjectConfig, } from './project-generator/index.js';
68
70
  export { readFileTool, writeFileTool, createBashTool, bashTool, bashOutputTool, killShellTool, grepTool, globTool, editTool, todoWriteTool, todoReadTool, createTodoTools, TodoStore, webFetchTool, suggestTool, } from '@compilr-dev/agents';
69
71
  export { gitStatusTool, gitDiffTool, gitLogTool, gitCommitTool, gitBranchTool, gitStashTool, gitBlameTool, gitFileHistoryTool, detectProjectTool, findProjectRootTool, runTestsTool, runLintTool, runBuildTool, runFormatTool, findDefinitionTool, findReferencesTool, findTodosTool, checkOutdatedTool, findVulnerabilitiesTool, analyzeTestCoverageTool, getFileStructureTool, getComplexityTool, allCodingTools, unifiedTools, } from '@compilr-dev/agents-coding';
package/dist/index.js CHANGED
@@ -165,6 +165,18 @@ export { DEFAULT_PERMISSION_RULES, findMatchingRule, permissionModeLabel, permis
165
165
  // =============================================================================
166
166
  export { readMCPConfigFile, writeMCPConfigFile, resolveServerEntry, loadMCPServers, saveMCPServerEntry, deleteMCPServerEntry, getServerNames, } from './mcp-config.js';
167
167
  // =============================================================================
168
+ // Project Generator (templates, scaffolding, detection)
169
+ // =============================================================================
170
+ export {
171
+ // Generator
172
+ generateProject, isGitConfigured,
173
+ // Templates
174
+ generateCompilrMd, generateConfigJson, generateReadmeMd, generateCodingStandardsMd, generatePackageJson, generateTsconfig, generateGitignore, generateCompilrMdForImport,
175
+ // Detection
176
+ detectProjectInfo, detectGitInfo, prettifyName, getLanguageLabel, getFrameworkLabel, validateImportPath, isValidProjectName, projectExists,
177
+ // Constants
178
+ TECH_STACK_LABELS, CODING_STANDARDS_LABELS, REPO_PATTERN_LABELS, WORKFLOW_VERSION, } from './project-generator/index.js';
179
+ // =============================================================================
168
180
  // Individual Tool Re-exports (for consumers that build custom tool registries)
169
181
  // =============================================================================
170
182
  // Base tools from @compilr-dev/agents
@@ -4,6 +4,7 @@
4
4
  * Uses better-sqlite3 (optional peer dependency).
5
5
  * Consumers must install better-sqlite3 to use the SQLite repositories.
6
6
  */
7
+ import { createRequire } from 'module';
7
8
  import * as fs from 'fs';
8
9
  import * as path from 'path';
9
10
  import { SCHEMA_SQL, SCHEMA_VERSION } from './schema.js';
@@ -17,9 +18,10 @@ export function getDatabase(dbPath) {
17
18
  const existing = instances.get(dbPath);
18
19
  if (existing)
19
20
  return existing;
20
- // Dynamic require of better-sqlite3 — it's an optional peer dep
21
- // eslint-disable-next-line @typescript-eslint/no-require-imports
22
- const BetterSqlite3 = require('better-sqlite3');
21
+ // Dynamic require of better-sqlite3 — it's an optional peer dep.
22
+ // Use createRequire() for ESM compatibility (works in Node.js and Electron).
23
+ const esmRequire = createRequire(import.meta.url);
24
+ const BetterSqlite3 = esmRequire('better-sqlite3');
23
25
  // Ensure directory exists
24
26
  fs.mkdirSync(path.dirname(dbPath), { recursive: true });
25
27
  const db = new BetterSqlite3(dbPath);
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Project Detection Utilities
3
+ *
4
+ * Utilities for detecting project information from an existing directory.
5
+ * Used by the import project feature.
6
+ */
7
+ import type { DetectedProject, GitInfo } from './types.js';
8
+ /**
9
+ * Detect project information from a directory path
10
+ */
11
+ export declare function detectProjectInfo(projectPath: string): DetectedProject;
12
+ /**
13
+ * Detect git repository information
14
+ */
15
+ export declare function detectGitInfo(projectPath: string): GitInfo;
16
+ /**
17
+ * Convert kebab-case or snake_case to Title Case
18
+ */
19
+ export declare function prettifyName(name: string): string;
20
+ /**
21
+ * Get a human-readable label for detected language
22
+ */
23
+ export declare function getLanguageLabel(language: string | null): string;
24
+ /**
25
+ * Get a human-readable label for detected framework
26
+ */
27
+ export declare function getFrameworkLabel(framework: string | null): string;
28
+ /**
29
+ * Check if a path is a valid directory that can be imported
30
+ */
31
+ export declare function validateImportPath(importPath: string): {
32
+ valid: boolean;
33
+ error?: string;
34
+ };
35
+ /**
36
+ * Validate project name
37
+ */
38
+ export declare function isValidProjectName(name: string): boolean;
39
+ /**
40
+ * Check if project directory already exists
41
+ */
42
+ export declare function projectExists(name: string, basePath: string): boolean;
@@ -0,0 +1,401 @@
1
+ /**
2
+ * Project Detection Utilities
3
+ *
4
+ * Utilities for detecting project information from an existing directory.
5
+ * Used by the import project feature.
6
+ */
7
+ import * as fs from 'fs';
8
+ import * as path from 'path';
9
+ import { execSync } from 'child_process';
10
+ const LANGUAGE_MARKERS = [
11
+ // Node.js / JavaScript / TypeScript
12
+ {
13
+ file: 'package.json',
14
+ language: 'node',
15
+ packageManager: 'npm',
16
+ frameworkDetector: (content) => {
17
+ try {
18
+ const pkg = JSON.parse(content);
19
+ const deps = { ...pkg.dependencies, ...pkg.devDependencies };
20
+ if (deps['next'])
21
+ return 'next';
22
+ if (deps['nuxt'])
23
+ return 'nuxt';
24
+ if (deps['react'])
25
+ return 'react';
26
+ if (deps['vue'])
27
+ return 'vue';
28
+ if (deps['svelte'])
29
+ return 'svelte';
30
+ if (deps['@angular/core'])
31
+ return 'angular';
32
+ if (deps['express'])
33
+ return 'express';
34
+ if (deps['fastify'])
35
+ return 'fastify';
36
+ if (deps['koa'])
37
+ return 'koa';
38
+ if (deps['hono'])
39
+ return 'hono';
40
+ if (deps['electron'])
41
+ return 'electron';
42
+ return null;
43
+ }
44
+ catch {
45
+ return null;
46
+ }
47
+ },
48
+ nameExtractor: (content) => {
49
+ try {
50
+ const pkg = JSON.parse(content);
51
+ return pkg.name ?? null;
52
+ }
53
+ catch {
54
+ return null;
55
+ }
56
+ },
57
+ },
58
+ // Go
59
+ {
60
+ file: 'go.mod',
61
+ language: 'go',
62
+ packageManager: 'go',
63
+ nameExtractor: (content) => {
64
+ const match = content.match(/^module\s+(.+)$/m);
65
+ if (match) {
66
+ const parts = match[1].split('/');
67
+ return parts[parts.length - 1];
68
+ }
69
+ return null;
70
+ },
71
+ },
72
+ // Python
73
+ {
74
+ file: 'pyproject.toml',
75
+ language: 'python',
76
+ packageManager: 'pip',
77
+ frameworkDetector: (content) => {
78
+ if (content.includes('fastapi'))
79
+ return 'fastapi';
80
+ if (content.includes('django'))
81
+ return 'django';
82
+ if (content.includes('flask'))
83
+ return 'flask';
84
+ return null;
85
+ },
86
+ nameExtractor: (content) => {
87
+ const match = content.match(/^name\s*=\s*["'](.+)["']/m);
88
+ return match ? match[1] : null;
89
+ },
90
+ },
91
+ {
92
+ file: 'requirements.txt',
93
+ language: 'python',
94
+ packageManager: 'pip',
95
+ frameworkDetector: (content) => {
96
+ if (content.includes('fastapi'))
97
+ return 'fastapi';
98
+ if (content.includes('django'))
99
+ return 'django';
100
+ if (content.includes('flask'))
101
+ return 'flask';
102
+ return null;
103
+ },
104
+ },
105
+ {
106
+ file: 'setup.py',
107
+ language: 'python',
108
+ packageManager: 'pip',
109
+ },
110
+ // Rust
111
+ {
112
+ file: 'Cargo.toml',
113
+ language: 'rust',
114
+ packageManager: 'cargo',
115
+ nameExtractor: (content) => {
116
+ const match = content.match(/^name\s*=\s*["'](.+)["']/m);
117
+ return match ? match[1] : null;
118
+ },
119
+ },
120
+ // Java
121
+ { file: 'pom.xml', language: 'java', packageManager: 'maven' },
122
+ { file: 'build.gradle', language: 'java', packageManager: 'gradle' },
123
+ { file: 'build.gradle.kts', language: 'kotlin', packageManager: 'gradle' },
124
+ // PHP
125
+ {
126
+ file: 'composer.json',
127
+ language: 'php',
128
+ packageManager: 'composer',
129
+ frameworkDetector: (content) => {
130
+ try {
131
+ const pkg = JSON.parse(content);
132
+ const deps = pkg.require ?? {};
133
+ if (deps['laravel/framework'])
134
+ return 'laravel';
135
+ if (deps['symfony/framework-bundle'])
136
+ return 'symfony';
137
+ return null;
138
+ }
139
+ catch {
140
+ return null;
141
+ }
142
+ },
143
+ nameExtractor: (content) => {
144
+ try {
145
+ const pkg = JSON.parse(content);
146
+ if (pkg.name) {
147
+ const parts = pkg.name.split('/');
148
+ return parts[parts.length - 1];
149
+ }
150
+ return null;
151
+ }
152
+ catch {
153
+ return null;
154
+ }
155
+ },
156
+ },
157
+ // Ruby
158
+ {
159
+ file: 'Gemfile',
160
+ language: 'ruby',
161
+ packageManager: 'bundler',
162
+ frameworkDetector: (content) => {
163
+ if (content.includes("'rails'") || content.includes('"rails"'))
164
+ return 'rails';
165
+ if (content.includes("'sinatra'") || content.includes('"sinatra"'))
166
+ return 'sinatra';
167
+ return null;
168
+ },
169
+ },
170
+ // .NET
171
+ { file: '*.csproj', language: 'dotnet', packageManager: 'nuget' },
172
+ { file: '*.fsproj', language: 'dotnet', packageManager: 'nuget' },
173
+ ];
174
+ // =============================================================================
175
+ // Main Detection Functions
176
+ // =============================================================================
177
+ /**
178
+ * Detect project information from a directory path
179
+ */
180
+ export function detectProjectInfo(projectPath) {
181
+ const folderName = path.basename(projectPath);
182
+ const result = {
183
+ name: folderName,
184
+ displayName: prettifyName(folderName),
185
+ nameSource: 'folder',
186
+ language: null,
187
+ framework: null,
188
+ packageManager: null,
189
+ hasGit: false,
190
+ gitRemote: null,
191
+ gitBranch: null,
192
+ hasCompilrMd: false,
193
+ hasClaudeMd: false,
194
+ };
195
+ // Check for COMPILR.md and CLAUDE.md
196
+ result.hasCompilrMd = fs.existsSync(path.join(projectPath, 'COMPILR.md'));
197
+ result.hasClaudeMd = fs.existsSync(path.join(projectPath, 'CLAUDE.md'));
198
+ // Detect git info
199
+ const gitInfo = detectGitInfo(projectPath);
200
+ result.hasGit = gitInfo.hasGit;
201
+ result.gitRemote = gitInfo.remote;
202
+ result.gitBranch = gitInfo.branch;
203
+ // Detect language and framework from markers
204
+ for (const marker of LANGUAGE_MARKERS) {
205
+ let filePath = null;
206
+ if (marker.file.includes('*')) {
207
+ const pattern = marker.file.replace('*', '');
208
+ try {
209
+ const files = fs.readdirSync(projectPath);
210
+ const match = files.find((f) => f.endsWith(pattern));
211
+ if (match) {
212
+ filePath = path.join(projectPath, match);
213
+ }
214
+ }
215
+ catch {
216
+ continue;
217
+ }
218
+ }
219
+ else {
220
+ filePath = path.join(projectPath, marker.file);
221
+ }
222
+ if (filePath && fs.existsSync(filePath)) {
223
+ if (!result.language) {
224
+ result.language = marker.language;
225
+ }
226
+ try {
227
+ const content = fs.readFileSync(filePath, 'utf-8');
228
+ if (marker.nameExtractor && result.nameSource === 'folder') {
229
+ const extractedName = marker.nameExtractor(content);
230
+ if (extractedName) {
231
+ result.name = extractedName;
232
+ result.displayName = prettifyName(extractedName);
233
+ result.nameSource = marker.file;
234
+ }
235
+ }
236
+ if (!result.framework && marker.frameworkDetector) {
237
+ result.framework = marker.frameworkDetector(content);
238
+ }
239
+ if (!result.packageManager && marker.packageManager) {
240
+ result.packageManager = marker.packageManager;
241
+ }
242
+ }
243
+ catch {
244
+ // File read failed, continue
245
+ }
246
+ }
247
+ }
248
+ // Detect package manager overrides for Node.js
249
+ if (result.language === 'node') {
250
+ if (fs.existsSync(path.join(projectPath, 'pnpm-lock.yaml'))) {
251
+ result.packageManager = 'pnpm';
252
+ }
253
+ else if (fs.existsSync(path.join(projectPath, 'yarn.lock'))) {
254
+ result.packageManager = 'yarn';
255
+ }
256
+ else if (fs.existsSync(path.join(projectPath, 'bun.lockb'))) {
257
+ result.packageManager = 'bun';
258
+ }
259
+ }
260
+ // Detect package manager overrides for Python
261
+ if (result.language === 'python') {
262
+ if (fs.existsSync(path.join(projectPath, 'poetry.lock'))) {
263
+ result.packageManager = 'poetry';
264
+ }
265
+ else if (fs.existsSync(path.join(projectPath, 'pdm.lock'))) {
266
+ result.packageManager = 'pdm';
267
+ }
268
+ else if (fs.existsSync(path.join(projectPath, 'uv.lock'))) {
269
+ result.packageManager = 'uv';
270
+ }
271
+ }
272
+ return result;
273
+ }
274
+ /**
275
+ * Detect git repository information
276
+ */
277
+ export function detectGitInfo(projectPath) {
278
+ const result = {
279
+ hasGit: false,
280
+ remote: null,
281
+ branch: null,
282
+ };
283
+ const gitPath = path.join(projectPath, '.git');
284
+ if (!fs.existsSync(gitPath)) {
285
+ return result;
286
+ }
287
+ result.hasGit = true;
288
+ try {
289
+ const remote = execSync('git remote get-url origin', {
290
+ cwd: projectPath,
291
+ encoding: 'utf8',
292
+ stdio: ['pipe', 'pipe', 'pipe'],
293
+ }).trim();
294
+ if (remote) {
295
+ result.remote = remote;
296
+ }
297
+ }
298
+ catch {
299
+ // No remote configured
300
+ }
301
+ try {
302
+ const branch = execSync('git branch --show-current', {
303
+ cwd: projectPath,
304
+ encoding: 'utf8',
305
+ stdio: ['pipe', 'pipe', 'pipe'],
306
+ }).trim();
307
+ if (branch) {
308
+ result.branch = branch;
309
+ }
310
+ }
311
+ catch {
312
+ // Detached HEAD or git command failed
313
+ }
314
+ return result;
315
+ }
316
+ // =============================================================================
317
+ // Helper Functions
318
+ // =============================================================================
319
+ /**
320
+ * Convert kebab-case or snake_case to Title Case
321
+ */
322
+ export function prettifyName(name) {
323
+ return name.replace(/[-_]/g, ' ').replace(/\b\w/g, (c) => c.toUpperCase());
324
+ }
325
+ /**
326
+ * Get a human-readable label for detected language
327
+ */
328
+ export function getLanguageLabel(language) {
329
+ if (!language)
330
+ return 'Unknown';
331
+ const labels = {
332
+ node: 'Node.js / TypeScript',
333
+ go: 'Go',
334
+ python: 'Python',
335
+ rust: 'Rust',
336
+ java: 'Java',
337
+ kotlin: 'Kotlin',
338
+ php: 'PHP',
339
+ ruby: 'Ruby',
340
+ dotnet: '.NET (C#/F#)',
341
+ };
342
+ return labels[language] ?? language;
343
+ }
344
+ /**
345
+ * Get a human-readable label for detected framework
346
+ */
347
+ export function getFrameworkLabel(framework) {
348
+ if (!framework)
349
+ return 'Not detected';
350
+ const labels = {
351
+ react: 'React',
352
+ vue: 'Vue.js',
353
+ svelte: 'Svelte',
354
+ angular: 'Angular',
355
+ next: 'Next.js',
356
+ nuxt: 'Nuxt.js',
357
+ express: 'Express.js',
358
+ fastify: 'Fastify',
359
+ koa: 'Koa',
360
+ hono: 'Hono',
361
+ electron: 'Electron',
362
+ fastapi: 'FastAPI',
363
+ django: 'Django',
364
+ flask: 'Flask',
365
+ rails: 'Ruby on Rails',
366
+ sinatra: 'Sinatra',
367
+ laravel: 'Laravel',
368
+ symfony: 'Symfony',
369
+ };
370
+ return labels[framework] ?? framework;
371
+ }
372
+ /**
373
+ * Check if a path is a valid directory that can be imported
374
+ */
375
+ export function validateImportPath(importPath) {
376
+ if (!fs.existsSync(importPath)) {
377
+ return { valid: false, error: 'Path does not exist' };
378
+ }
379
+ try {
380
+ const stats = fs.statSync(importPath);
381
+ if (!stats.isDirectory()) {
382
+ return { valid: false, error: 'Path is not a directory' };
383
+ }
384
+ }
385
+ catch {
386
+ return { valid: false, error: 'Cannot access path' };
387
+ }
388
+ return { valid: true };
389
+ }
390
+ /**
391
+ * Validate project name
392
+ */
393
+ export function isValidProjectName(name) {
394
+ return /^[a-z][a-z0-9-]{1,49}$/.test(name);
395
+ }
396
+ /**
397
+ * Check if project directory already exists
398
+ */
399
+ export function projectExists(name, basePath) {
400
+ return fs.existsSync(path.join(basePath, name));
401
+ }
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Project Generator
3
+ *
4
+ * Main module for generating project scaffolding.
5
+ */
6
+ import type { ProjectConfig, GenerationResult } from './types.js';
7
+ /**
8
+ * Generate a new project from configuration
9
+ */
10
+ export declare function generateProject(config: ProjectConfig, basePath: string): GenerationResult;
11
+ /**
12
+ * Check if git user.name and user.email are configured
13
+ */
14
+ export declare function isGitConfigured(): boolean;