@pimzino/claude-code-spec-workflow
Version:
Automated workflows for Claude Code. Includes spec-driven development (Requirements → Design → Tasks → Implementation) with intelligent task execution, optional steering documents and streamlined bug fix workflow (Report → Analyze → Fix → Verify). We have
173 lines • 5.37 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.detectProjectType = detectProjectType;
exports.validateClaudeCode = validateClaudeCode;
exports.fileExists = fileExists;
exports.ensureDirectory = ensureDirectory;
exports.isPortAvailable = isPortAvailable;
exports.findAvailablePort = findAvailablePort;
exports.getBestAvailablePort = getBestAvailablePort;
const fs_1 = require("fs");
const path_1 = require("path");
const child_process_1 = require("child_process");
const util_1 = require("util");
const net_1 = require("net");
const execAsync = (0, util_1.promisify)(child_process_1.exec);
/**
* Detects project type(s) based on files present in the project directory.
* Supports multiple project types including Node.js, Python, Java, C#, Go, Rust, PHP, and Ruby.
*
* @param projectPath - Path to the project directory to analyze
* @returns Promise resolving to array of detected project type names
*
* @example
* ```typescript
* const types = await detectProjectType('/path/to/project');
* console.log(types); // ['Node.js', 'TypeScript']
* ```
*/
async function detectProjectType(projectPath) {
const indicators = {
'Node.js': ['package.json', 'node_modules'],
Python: ['requirements.txt', 'setup.py', 'pyproject.toml', '__pycache__'],
Java: ['pom.xml', 'build.gradle'],
'C#': ['*.csproj', '*.sln'],
Go: ['go.mod', 'go.sum'],
Rust: ['Cargo.toml', 'Cargo.lock'],
PHP: ['composer.json', 'vendor'],
Ruby: ['Gemfile', 'Gemfile.lock'],
};
const detected = [];
for (const [projectType, files] of Object.entries(indicators)) {
for (const file of files) {
try {
if (file.includes('*')) {
// Handle glob patterns - simplified check
const dirContents = await fs_1.promises.readdir(projectPath);
const extension = file.replace('*', '');
if (dirContents.some((f) => f.endsWith(extension))) {
detected.push(projectType);
break;
}
}
else {
await fs_1.promises.access((0, path_1.join)(projectPath, file));
detected.push(projectType);
break;
}
}
catch {
// File doesn't exist, continue
}
}
}
return detected;
}
/**
* Validates that Claude Code CLI is installed and accessible.
*
* @returns Promise resolving to true if Claude Code is available, false otherwise
*
* @example
* ```typescript
* const isInstalled = await validateClaudeCode();
* if (!isInstalled) {
* console.log('Please install Claude Code first');
* }
* ```
*/
async function validateClaudeCode() {
try {
await execAsync('claude --version');
return true;
}
catch {
return false;
}
}
/**
* Checks if a file exists at the given path.
*
* @param filePath - Path to the file to check
* @returns Promise resolving to true if file exists, false otherwise
*
* @example
* ```typescript
* const exists = await fileExists('package.json');
* if (exists) {
* console.log('Found package.json');
* }
* ```
*/
async function fileExists(filePath) {
try {
await fs_1.promises.access(filePath);
return true;
}
catch {
return false;
}
}
/**
* Ensures a directory exists by creating it recursively if needed.
*
* @param dirPath - Path to the directory to create
* @throws Error if directory creation fails for reasons other than already existing
*
* @example
* ```typescript
* await ensureDirectory('.claude/specs/my-feature');
* // Directory structure is now guaranteed to exist
* ```
*/
async function ensureDirectory(dirPath) {
try {
await fs_1.promises.mkdir(dirPath, { recursive: true });
}
catch (error) {
if (error instanceof Error &&
'code' in error &&
error.code !== 'EEXIST') {
throw error;
}
}
}
/**
* Check if a port is available
*/
async function isPortAvailable(port) {
return new Promise((resolve) => {
const server = (0, net_1.createServer)();
server.listen(port, () => {
server.close(() => resolve(true));
});
server.on('error', () => {
resolve(false);
});
});
}
/**
* Find an available port starting from a given port number
*/
async function findAvailablePort(startPort = 3000, maxAttempts = 100) {
for (let port = startPort; port < startPort + maxAttempts; port++) {
if (await isPortAvailable(port)) {
return port;
}
}
throw new Error(`Could not find an available port after checking ${maxAttempts} ports starting from ${startPort}`);
}
/**
* Get the best available port from a list of preferred ports, with fallback
*/
async function getBestAvailablePort(preferredPorts = [3000, 3001, 3002, 8080, 8000, 4000]) {
// First try the preferred ports
for (const port of preferredPorts) {
if (await isPortAvailable(port)) {
return port;
}
}
// Fall back to finding any available port starting from 3000
return findAvailablePort(3000);
}
//# sourceMappingURL=utils.js.map