aiwg
Version:
Deployment tool and support utility for AI context. Copies agents, skills, commands, rules, and behaviors into the paths each AI platform reads (Claude Code, Codex, Copilot, Cursor, Warp, OpenClaw, and 6 more) so one source of truth works across 10 platfo
165 lines • 5.56 kB
JavaScript
/**
* FrameworkDetector - Detect framework-scoped workspaces
*
* Detects frameworks from directory structure (.claude/, .codex/, .cursor/)
* and configuration files. Distinguishes legacy workspaces from framework-scoped.
*
* FID-007 Framework-Scoped Workspaces detection logic.
*
* @module src/plugin/framework-detector
* @version 1.0.0
* @since 2025-10-23
*/
import * as fs from 'fs/promises';
import * as path from 'path';
// ===========================
// FrameworkDetector Class
// ===========================
export class FrameworkDetector {
projectRoot;
FRAMEWORK_DIRS = ['claude', 'codex', 'cursor'];
LEGACY_DIRS = [
// SDLC shared directories
'intake', 'requirements', 'architecture', 'planning',
'risks', 'testing', 'security', 'quality', 'deployment',
'reports', 'working', 'handoffs', 'gates', 'decisions', 'team', 'management',
// Framework-specific directories (legacy when directly under .aiwg)
'agents', 'commands', 'memory', 'context'
];
constructor(projectRoot) {
this.projectRoot = path.resolve(projectRoot);
}
/**
* Detect all frameworks present in the project
*
* @returns Array of framework names
*/
async detectFrameworks() {
const frameworks = new Set();
// Check for .framework/ directories
for (const framework of this.FRAMEWORK_DIRS) {
try {
await fs.access(path.join(this.projectRoot, `.${framework}`));
frameworks.add(framework);
}
catch {
// Framework directory doesn't exist
}
}
// Check for .aiwg/framework/ directories
const aiwgPath = path.join(this.projectRoot, '.aiwg');
try {
const entries = await fs.readdir(aiwgPath, { withFileTypes: true });
for (const entry of entries) {
if (entry.isDirectory() && this.FRAMEWORK_DIRS.includes(entry.name)) {
frameworks.add(entry.name);
}
}
}
catch {
// .aiwg doesn't exist
}
return Array.from(frameworks).sort();
}
/**
* Check if workspace is legacy (non-framework-scoped)
*
* @returns True if legacy workspace detected
*/
async isLegacyWorkspace() {
const aiwgPath = path.join(this.projectRoot, '.aiwg');
try {
await fs.access(aiwgPath);
}
catch {
return false; // No .aiwg directory
}
// Check for legacy SDLC directories
let legacyDirCount = 0;
let frameworkDirCount = 0;
try {
const entries = await fs.readdir(aiwgPath, { withFileTypes: true });
for (const entry of entries) {
if (entry.isDirectory()) {
if (this.LEGACY_DIRS.includes(entry.name)) {
legacyDirCount++;
}
if (this.FRAMEWORK_DIRS.includes(entry.name)) {
frameworkDirCount++;
}
}
}
}
catch {
return false;
}
// Legacy workspace if has legacy dirs but no framework dirs
return legacyDirCount > 0 && frameworkDirCount === 0;
}
/**
* Get detailed information about a framework
*
* @param frameworkName - Framework name
* @returns Framework information
*/
async getFrameworkInfo(frameworkName) {
// Check .aiwg/framework/ first
const aiwgPath = path.join(this.projectRoot, '.aiwg', frameworkName);
const rootPath = path.join(this.projectRoot, `.${frameworkName}`);
let frameworkPath;
let configPath;
try {
await fs.access(aiwgPath);
frameworkPath = aiwgPath;
configPath = path.join(aiwgPath, 'settings.json');
}
catch {
try {
await fs.access(rootPath);
frameworkPath = rootPath;
configPath = path.join(rootPath, 'settings.json');
}
catch {
throw new Error(`Framework not found: ${frameworkName}`);
}
}
// Load config
let config = {};
try {
const configContent = await fs.readFile(configPath, 'utf-8');
config = JSON.parse(configContent);
}
catch {
// Config doesn't exist or is invalid, use defaults
}
// Count agents and commands
let agentCount = 0;
let commandCount = 0;
try {
const agentsPath = path.join(frameworkPath, 'agents');
const agents = await fs.readdir(agentsPath);
agentCount = agents.filter(f => f.endsWith('.md')).length;
}
catch {
// Agents directory doesn't exist
}
try {
const commandsPath = path.join(frameworkPath, 'commands');
const commands = await fs.readdir(commandsPath);
commandCount = commands.filter(f => f.endsWith('.md')).length;
}
catch {
// Commands directory doesn't exist
}
return {
name: frameworkName,
path: frameworkPath,
type: 'ide',
version: config.version,
capabilities: config.capabilities,
agentCount,
commandCount
};
}
}
//# sourceMappingURL=framework-detector.js.map