@qlucent/fishi-core 0.7.0 → 0.9.0
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.
- package/dist/index.d.ts +86 -1
- package/dist/index.js +421 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { ChildProcess } from 'child_process';
|
|
2
|
+
|
|
1
3
|
type ProjectType = 'greenfield' | 'brownfield' | 'hybrid';
|
|
2
4
|
type CostMode = 'performance' | 'balanced' | 'economy';
|
|
3
5
|
type ModelTier = 'opus' | 'sonnet' | 'haiku';
|
|
@@ -620,10 +622,93 @@ declare function runInSandbox(command: string, args: string[], worktreePath: str
|
|
|
620
622
|
nodeModulesPath?: string;
|
|
621
623
|
}): SandboxRunResult;
|
|
622
624
|
|
|
625
|
+
interface DevServerConfig {
|
|
626
|
+
command: string;
|
|
627
|
+
args: string[];
|
|
628
|
+
port: number;
|
|
629
|
+
framework: string;
|
|
630
|
+
detected: boolean;
|
|
631
|
+
}
|
|
632
|
+
/**
|
|
633
|
+
* Detect the dev server command from package.json scripts or framework detection.
|
|
634
|
+
*/
|
|
635
|
+
declare function detectDevServer(projectDir: string, customCmd?: string): DevServerConfig;
|
|
636
|
+
/**
|
|
637
|
+
* Start the dev server as a background process.
|
|
638
|
+
* Returns the child process handle.
|
|
639
|
+
*/
|
|
640
|
+
declare function startDevServer(projectDir: string, config: DevServerConfig): ChildProcess;
|
|
641
|
+
/**
|
|
642
|
+
* Get the vibe mode config for fishi.yaml.
|
|
643
|
+
*/
|
|
644
|
+
declare function getVibeModeConfig(enabled: boolean): string;
|
|
645
|
+
|
|
646
|
+
interface DesignTokens {
|
|
647
|
+
colors: Record<string, string>;
|
|
648
|
+
typography: {
|
|
649
|
+
fontFamilies: string[];
|
|
650
|
+
scale: Record<string, string>;
|
|
651
|
+
};
|
|
652
|
+
spacing: Record<string, string>;
|
|
653
|
+
borderRadius: Record<string, string>;
|
|
654
|
+
shadows: Record<string, string>;
|
|
655
|
+
darkMode: boolean;
|
|
656
|
+
}
|
|
657
|
+
interface ComponentEntry {
|
|
658
|
+
name: string;
|
|
659
|
+
path: string;
|
|
660
|
+
type: 'ui' | 'layout' | 'form' | 'data' | 'navigation' | 'other';
|
|
661
|
+
}
|
|
662
|
+
interface ComponentRegistry {
|
|
663
|
+
components: ComponentEntry[];
|
|
664
|
+
library: string | null;
|
|
665
|
+
framework: string | null;
|
|
666
|
+
}
|
|
667
|
+
interface BrandGuardianIssue {
|
|
668
|
+
file: string;
|
|
669
|
+
line: number;
|
|
670
|
+
severity: 'error' | 'warning' | 'info';
|
|
671
|
+
rule: string;
|
|
672
|
+
message: string;
|
|
673
|
+
fix?: string;
|
|
674
|
+
}
|
|
675
|
+
interface BrandGuardianReport {
|
|
676
|
+
issues: BrandGuardianIssue[];
|
|
677
|
+
passed: boolean;
|
|
678
|
+
stats: {
|
|
679
|
+
errors: number;
|
|
680
|
+
warnings: number;
|
|
681
|
+
infos: number;
|
|
682
|
+
filesScanned: number;
|
|
683
|
+
};
|
|
684
|
+
}
|
|
685
|
+
/**
|
|
686
|
+
* Detect design tokens from a project's config files.
|
|
687
|
+
* Checks: tailwind.config, CSS custom properties, theme files.
|
|
688
|
+
*/
|
|
689
|
+
declare function detectDesignTokens(projectDir: string): DesignTokens;
|
|
690
|
+
/**
|
|
691
|
+
* Generate default design tokens for a new project.
|
|
692
|
+
*/
|
|
693
|
+
declare function generateDefaultTokens(): DesignTokens;
|
|
694
|
+
/**
|
|
695
|
+
* Detect component library and registry from a project.
|
|
696
|
+
*/
|
|
697
|
+
declare function detectComponentRegistry(projectDir: string): ComponentRegistry;
|
|
698
|
+
/**
|
|
699
|
+
* Run Brand Guardian validation on project files.
|
|
700
|
+
* Checks for hardcoded colors, inconsistent spacing, missing a11y, etc.
|
|
701
|
+
*/
|
|
702
|
+
declare function runBrandGuardian(projectDir: string, tokens: DesignTokens): BrandGuardianReport;
|
|
703
|
+
/**
|
|
704
|
+
* Generate a design system config file for the project.
|
|
705
|
+
*/
|
|
706
|
+
declare function generateDesignSystemConfig(tokens: DesignTokens, registry: ComponentRegistry): string;
|
|
707
|
+
|
|
623
708
|
declare function getSandboxPolicyTemplate(): string;
|
|
624
709
|
|
|
625
710
|
declare function getDockerfileTemplate(): string;
|
|
626
711
|
|
|
627
712
|
declare function getDashboardHtml(): string;
|
|
628
713
|
|
|
629
|
-
export { type AgentDefinition, type AgentRole, type AgentTemplate, type BackupManifest, type BrownfieldAnalysisData, type ClaudeMdOptions, type CommandTemplate, type ConflictCategory, type ConflictMap, type ConflictResolution, type CostMode, type DetectionCheck, type DetectionResult, type DynamicAgent, type DynamicAgentConfig, type ExecutionConfig, type FileConflict, type FileResolutionMap, type FishiConfig, type FishiYamlOptions, type GateConfig, type GateStatus, type GitConfig, type HookTemplate, type InitOptions, type McpConfig, type McpServerConfig, type ModelRoutingConfig, type ModelTier, type MonitorEvent, type MonitorState, type MonitorSummary, type ProjectConfig, type ProjectType, type ProjectYamlOptions, type SandboxConfig, type SandboxMode, type SandboxPolicy, type SandboxRunResult, type ScaffoldOptions, type ScaffoldResult, type SkillTemplate, type StateConfig, type TaskStatus, type TaskboardConfig, type TemplateContext, architectAgentTemplate, backendAgentTemplate, buildSandboxEnv, createBackup, detectConflicts, detectDocker, devLeadTemplate, devopsAgentTemplate, docsAgentTemplate, emitEvent, frontendAgentTemplate, fullstackAgentTemplate, generateScaffold, getAdaptiveTaskGraphSkill, getAgentCompleteHook, getAgentFactoryTemplate, getAgentRegistryTemplate, getAgentSummary, getApiDesignSkill, getAutoCheckpointHook, getBoardCommand, getBrainstormingSkill, getBrownfieldAnalysisSkill, getBrownfieldDiscoverySkill, getClaudeMdTemplate, getCodeGenSkill, getCoordinatorFactoryTemplate, getDashboardHtml, getDebuggingSkill, getDeploymentSkill, getDocCheckerScript, getDockerfileTemplate, getDocumentationSkill, getFishiYamlTemplate, getGateCommand, getGateManagerScript, getGitignoreAdditions, getInitCommand, getLearningsManagerScript, getMasterOrchestratorTemplate, getMcpJsonTemplate, getMemoryManagerScript, getModelRoutingReference, getMonitorEmitterScript, getPhaseRunnerScript, getPostEditHook, getPrdCommand, getPrdSkill, getProjectYamlTemplate, getResetCommand, getResumeCommand, getSafetyCheckHook, getSandboxPolicyTemplate, getSessionStartHook, getSettingsJsonTemplate, getSprintCommand, getStatusCommand, getTaskboardOpsSkill, getTaskboardUpdateHook, getTestingSkill, getTodoManagerScript, getValidateScaffoldScript, getWorktreeManagerScript, getWorktreeSetupHook, marketingAgentTemplate, mergeClaudeMd, mergeClaudeMdTop, mergeGitignore, mergeMcpJson, mergeSettingsJson, opsLeadTemplate, planningAgentTemplate, planningLeadTemplate, qualityLeadTemplate, readMonitorState, readSandboxConfig, readSandboxPolicy, researchAgentTemplate, runInDockerSandbox, runInProcessSandbox, runInSandbox, securityAgentTemplate, testingAgentTemplate, uiuxAgentTemplate, writingAgentTemplate };
|
|
714
|
+
export { type AgentDefinition, type AgentRole, type AgentTemplate, type BackupManifest, type BrandGuardianIssue, type BrandGuardianReport, type BrownfieldAnalysisData, type ClaudeMdOptions, type CommandTemplate, type ComponentEntry, type ComponentRegistry, type ConflictCategory, type ConflictMap, type ConflictResolution, type CostMode, type DesignTokens, type DetectionCheck, type DetectionResult, type DevServerConfig, type DynamicAgent, type DynamicAgentConfig, type ExecutionConfig, type FileConflict, type FileResolutionMap, type FishiConfig, type FishiYamlOptions, type GateConfig, type GateStatus, type GitConfig, type HookTemplate, type InitOptions, type McpConfig, type McpServerConfig, type ModelRoutingConfig, type ModelTier, type MonitorEvent, type MonitorState, type MonitorSummary, type ProjectConfig, type ProjectType, type ProjectYamlOptions, type SandboxConfig, type SandboxMode, type SandboxPolicy, type SandboxRunResult, type ScaffoldOptions, type ScaffoldResult, type SkillTemplate, type StateConfig, type TaskStatus, type TaskboardConfig, type TemplateContext, architectAgentTemplate, backendAgentTemplate, buildSandboxEnv, createBackup, detectComponentRegistry, detectConflicts, detectDesignTokens, detectDevServer, detectDocker, devLeadTemplate, devopsAgentTemplate, docsAgentTemplate, emitEvent, frontendAgentTemplate, fullstackAgentTemplate, generateDefaultTokens, generateDesignSystemConfig, generateScaffold, getAdaptiveTaskGraphSkill, getAgentCompleteHook, getAgentFactoryTemplate, getAgentRegistryTemplate, getAgentSummary, getApiDesignSkill, getAutoCheckpointHook, getBoardCommand, getBrainstormingSkill, getBrownfieldAnalysisSkill, getBrownfieldDiscoverySkill, getClaudeMdTemplate, getCodeGenSkill, getCoordinatorFactoryTemplate, getDashboardHtml, getDebuggingSkill, getDeploymentSkill, getDocCheckerScript, getDockerfileTemplate, getDocumentationSkill, getFishiYamlTemplate, getGateCommand, getGateManagerScript, getGitignoreAdditions, getInitCommand, getLearningsManagerScript, getMasterOrchestratorTemplate, getMcpJsonTemplate, getMemoryManagerScript, getModelRoutingReference, getMonitorEmitterScript, getPhaseRunnerScript, getPostEditHook, getPrdCommand, getPrdSkill, getProjectYamlTemplate, getResetCommand, getResumeCommand, getSafetyCheckHook, getSandboxPolicyTemplate, getSessionStartHook, getSettingsJsonTemplate, getSprintCommand, getStatusCommand, getTaskboardOpsSkill, getTaskboardUpdateHook, getTestingSkill, getTodoManagerScript, getValidateScaffoldScript, getVibeModeConfig, getWorktreeManagerScript, getWorktreeSetupHook, marketingAgentTemplate, mergeClaudeMd, mergeClaudeMdTop, mergeGitignore, mergeMcpJson, mergeSettingsJson, opsLeadTemplate, planningAgentTemplate, planningLeadTemplate, qualityLeadTemplate, readMonitorState, readSandboxConfig, readSandboxPolicy, researchAgentTemplate, runBrandGuardian, runInDockerSandbox, runInProcessSandbox, runInSandbox, securityAgentTemplate, startDevServer, testingAgentTemplate, uiuxAgentTemplate, writingAgentTemplate };
|
package/dist/index.js
CHANGED
|
@@ -11111,7 +11111,7 @@ async function createBackup(targetDir, conflictingFiles) {
|
|
|
11111
11111
|
manifestFiles.push({ path: relPath, size: stat.size });
|
|
11112
11112
|
}
|
|
11113
11113
|
}
|
|
11114
|
-
const fishiVersion = "0.
|
|
11114
|
+
const fishiVersion = "0.9.0";
|
|
11115
11115
|
const manifest = {
|
|
11116
11116
|
timestamp: now.toISOString(),
|
|
11117
11117
|
fishi_version: fishiVersion,
|
|
@@ -11368,6 +11368,418 @@ function runInSandbox(command, args, worktreePath, projectDir, options = {}) {
|
|
|
11368
11368
|
return runInProcessSandbox(command, args, worktreePath, policy);
|
|
11369
11369
|
}
|
|
11370
11370
|
|
|
11371
|
+
// src/generators/preview-server.ts
|
|
11372
|
+
import { existsSync as existsSync6, readFileSync as readFileSync3 } from "fs";
|
|
11373
|
+
import { join as join6 } from "path";
|
|
11374
|
+
import { spawn as spawn2 } from "child_process";
|
|
11375
|
+
function detectDevServer(projectDir, customCmd) {
|
|
11376
|
+
if (customCmd) {
|
|
11377
|
+
const parts = customCmd.split(/\s+/);
|
|
11378
|
+
return {
|
|
11379
|
+
command: parts[0],
|
|
11380
|
+
args: parts.slice(1),
|
|
11381
|
+
port: 3e3,
|
|
11382
|
+
framework: "custom",
|
|
11383
|
+
detected: true
|
|
11384
|
+
};
|
|
11385
|
+
}
|
|
11386
|
+
const pkgPath = join6(projectDir, "package.json");
|
|
11387
|
+
if (!existsSync6(pkgPath)) {
|
|
11388
|
+
if (existsSync6(join6(projectDir, "manage.py"))) {
|
|
11389
|
+
return { command: "python", args: ["manage.py", "runserver"], port: 8e3, framework: "django", detected: true };
|
|
11390
|
+
}
|
|
11391
|
+
if (existsSync6(join6(projectDir, "requirements.txt"))) {
|
|
11392
|
+
const reqs = readFileSync3(join6(projectDir, "requirements.txt"), "utf-8");
|
|
11393
|
+
if (reqs.includes("flask")) return { command: "python", args: ["-m", "flask", "run"], port: 5e3, framework: "flask", detected: true };
|
|
11394
|
+
if (reqs.includes("fastapi")) return { command: "uvicorn", args: ["main:app", "--reload"], port: 8e3, framework: "fastapi", detected: true };
|
|
11395
|
+
}
|
|
11396
|
+
return { command: "", args: [], port: 3e3, framework: "unknown", detected: false };
|
|
11397
|
+
}
|
|
11398
|
+
const pkg = JSON.parse(readFileSync3(pkgPath, "utf-8"));
|
|
11399
|
+
const scripts = pkg.scripts || {};
|
|
11400
|
+
if (scripts.dev) {
|
|
11401
|
+
const devCmd = scripts.dev;
|
|
11402
|
+
const port = extractPort(devCmd) || 3e3;
|
|
11403
|
+
const framework = detectFrameworkFromCmd(devCmd, pkg);
|
|
11404
|
+
return { command: "npm", args: ["run", "dev"], port, framework, detected: true };
|
|
11405
|
+
}
|
|
11406
|
+
if (scripts.start) {
|
|
11407
|
+
const port = extractPort(scripts.start) || 3e3;
|
|
11408
|
+
const framework = detectFrameworkFromCmd(scripts.start, pkg);
|
|
11409
|
+
return { command: "npm", args: ["run", "start"], port, framework, detected: true };
|
|
11410
|
+
}
|
|
11411
|
+
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
11412
|
+
if (deps["next"]) return { command: "npx", args: ["next", "dev"], port: 3e3, framework: "nextjs", detected: true };
|
|
11413
|
+
if (deps["vite"]) return { command: "npx", args: ["vite"], port: 5173, framework: "vite", detected: true };
|
|
11414
|
+
if (deps["astro"]) return { command: "npx", args: ["astro", "dev"], port: 4321, framework: "astro", detected: true };
|
|
11415
|
+
if (deps["nuxt"]) return { command: "npx", args: ["nuxt", "dev"], port: 3e3, framework: "nuxt", detected: true };
|
|
11416
|
+
if (deps["svelte-kit"] || deps["@sveltejs/kit"]) return { command: "npx", args: ["vite", "dev"], port: 5173, framework: "sveltekit", detected: true };
|
|
11417
|
+
if (deps["gatsby"]) return { command: "npx", args: ["gatsby", "develop"], port: 8e3, framework: "gatsby", detected: true };
|
|
11418
|
+
if (deps["remix"] || deps["@remix-run/dev"]) return { command: "npx", args: ["remix", "vite:dev"], port: 5173, framework: "remix", detected: true };
|
|
11419
|
+
if (deps["express"]) return { command: "node", args: ["index.js"], port: 3e3, framework: "express", detected: true };
|
|
11420
|
+
if (existsSync6(join6(projectDir, "manage.py"))) {
|
|
11421
|
+
return { command: "python", args: ["manage.py", "runserver"], port: 8e3, framework: "django", detected: true };
|
|
11422
|
+
}
|
|
11423
|
+
if (existsSync6(join6(projectDir, "requirements.txt"))) {
|
|
11424
|
+
const reqs = readFileSync3(join6(projectDir, "requirements.txt"), "utf-8");
|
|
11425
|
+
if (reqs.includes("flask")) return { command: "python", args: ["-m", "flask", "run"], port: 5e3, framework: "flask", detected: true };
|
|
11426
|
+
if (reqs.includes("fastapi")) return { command: "uvicorn", args: ["main:app", "--reload"], port: 8e3, framework: "fastapi", detected: true };
|
|
11427
|
+
}
|
|
11428
|
+
return { command: "", args: [], port: 3e3, framework: "unknown", detected: false };
|
|
11429
|
+
}
|
|
11430
|
+
function extractPort(cmd) {
|
|
11431
|
+
const portMatch = cmd.match(/(?:--port|-p)\s+(\d+)/) || cmd.match(/PORT=(\d+)/) || cmd.match(/:(\d{4,5})/);
|
|
11432
|
+
return portMatch ? parseInt(portMatch[1], 10) : null;
|
|
11433
|
+
}
|
|
11434
|
+
function detectFrameworkFromCmd(cmd, _pkg) {
|
|
11435
|
+
if (cmd.includes("next")) return "nextjs";
|
|
11436
|
+
if (cmd.includes("vite")) return "vite";
|
|
11437
|
+
if (cmd.includes("astro")) return "astro";
|
|
11438
|
+
if (cmd.includes("nuxt")) return "nuxt";
|
|
11439
|
+
if (cmd.includes("gatsby")) return "gatsby";
|
|
11440
|
+
if (cmd.includes("remix")) return "remix";
|
|
11441
|
+
if (cmd.includes("svelte")) return "sveltekit";
|
|
11442
|
+
if (cmd.includes("express") || cmd.includes("node server")) return "express";
|
|
11443
|
+
if (cmd.includes("flask")) return "flask";
|
|
11444
|
+
if (cmd.includes("django") || cmd.includes("manage.py")) return "django";
|
|
11445
|
+
if (cmd.includes("uvicorn") || cmd.includes("fastapi")) return "fastapi";
|
|
11446
|
+
return "unknown";
|
|
11447
|
+
}
|
|
11448
|
+
function startDevServer(projectDir, config) {
|
|
11449
|
+
const child = spawn2(config.command, config.args, {
|
|
11450
|
+
cwd: projectDir,
|
|
11451
|
+
stdio: "pipe",
|
|
11452
|
+
detached: false,
|
|
11453
|
+
env: { ...process.env, PORT: String(config.port) },
|
|
11454
|
+
shell: true
|
|
11455
|
+
});
|
|
11456
|
+
return child;
|
|
11457
|
+
}
|
|
11458
|
+
function getVibeModeConfig(enabled) {
|
|
11459
|
+
return `
|
|
11460
|
+
vibe_mode:
|
|
11461
|
+
enabled: ${enabled}
|
|
11462
|
+
auto_approve_gates: ${enabled}
|
|
11463
|
+
auto_generate_prd: ${enabled}
|
|
11464
|
+
background_testing: ${enabled}
|
|
11465
|
+
dev_server_autostart: ${enabled}
|
|
11466
|
+
`;
|
|
11467
|
+
}
|
|
11468
|
+
|
|
11469
|
+
// src/generators/design-system.ts
|
|
11470
|
+
import { existsSync as existsSync7, readFileSync as readFileSync4, readdirSync } from "fs";
|
|
11471
|
+
import { join as join7 } from "path";
|
|
11472
|
+
function detectDesignTokens(projectDir) {
|
|
11473
|
+
const tokens = {
|
|
11474
|
+
colors: {},
|
|
11475
|
+
typography: { fontFamilies: [], scale: {} },
|
|
11476
|
+
spacing: {},
|
|
11477
|
+
borderRadius: {},
|
|
11478
|
+
shadows: {},
|
|
11479
|
+
darkMode: false
|
|
11480
|
+
};
|
|
11481
|
+
const tailwindFiles = ["tailwind.config.js", "tailwind.config.ts", "tailwind.config.mjs", "tailwind.config.cjs"];
|
|
11482
|
+
for (const file of tailwindFiles) {
|
|
11483
|
+
const p = join7(projectDir, file);
|
|
11484
|
+
if (existsSync7(p)) {
|
|
11485
|
+
const content = readFileSync4(p, "utf-8");
|
|
11486
|
+
const colorMatches = content.matchAll(/['"]?([\w-]+)['"]?\s*:\s*['"]?(#[0-9a-fA-F]{3,8})['"]?/g);
|
|
11487
|
+
for (const m of colorMatches) {
|
|
11488
|
+
tokens.colors[m[1]] = m[2];
|
|
11489
|
+
}
|
|
11490
|
+
if (content.includes("darkMode")) tokens.darkMode = true;
|
|
11491
|
+
break;
|
|
11492
|
+
}
|
|
11493
|
+
}
|
|
11494
|
+
const cssFiles = findFiles(projectDir, ["src", "styles", "app"], [".css"]);
|
|
11495
|
+
for (const file of cssFiles.slice(0, 10)) {
|
|
11496
|
+
try {
|
|
11497
|
+
const content = readFileSync4(file, "utf-8");
|
|
11498
|
+
const varMatches = content.matchAll(/--(\w[\w-]*)\s*:\s*([^;]+)/g);
|
|
11499
|
+
for (const m of varMatches) {
|
|
11500
|
+
const name = m[1];
|
|
11501
|
+
const value = m[2].trim();
|
|
11502
|
+
if (name.includes("color") || name.includes("bg") || value.startsWith("#") || value.startsWith("rgb") || value.startsWith("hsl")) {
|
|
11503
|
+
tokens.colors[name] = value;
|
|
11504
|
+
} else if (name.includes("font")) {
|
|
11505
|
+
if (name.includes("size")) tokens.typography.scale[name] = value;
|
|
11506
|
+
else if (name.includes("family")) tokens.typography.fontFamilies.push(value);
|
|
11507
|
+
} else if (name.includes("spacing") || name.includes("gap") || name.includes("padding") || name.includes("margin")) {
|
|
11508
|
+
tokens.spacing[name] = value;
|
|
11509
|
+
} else if (name.includes("radius")) {
|
|
11510
|
+
tokens.borderRadius[name] = value;
|
|
11511
|
+
} else if (name.includes("shadow")) {
|
|
11512
|
+
tokens.shadows[name] = value;
|
|
11513
|
+
}
|
|
11514
|
+
}
|
|
11515
|
+
if (content.includes("prefers-color-scheme: dark") || content.includes(".dark")) {
|
|
11516
|
+
tokens.darkMode = true;
|
|
11517
|
+
}
|
|
11518
|
+
} catch {
|
|
11519
|
+
}
|
|
11520
|
+
}
|
|
11521
|
+
const themeFiles = ["theme.ts", "theme.js", "theme.json", "src/theme.ts", "src/theme.js", "src/styles/theme.ts"];
|
|
11522
|
+
for (const file of themeFiles) {
|
|
11523
|
+
if (existsSync7(join7(projectDir, file))) {
|
|
11524
|
+
try {
|
|
11525
|
+
const content = readFileSync4(join7(projectDir, file), "utf-8");
|
|
11526
|
+
const colorMatches = content.matchAll(/['"]?([\w-]+)['"]?\s*:\s*['"]?(#[0-9a-fA-F]{3,8})['"]?/g);
|
|
11527
|
+
for (const m of colorMatches) {
|
|
11528
|
+
tokens.colors[m[1]] = m[2];
|
|
11529
|
+
}
|
|
11530
|
+
} catch {
|
|
11531
|
+
}
|
|
11532
|
+
break;
|
|
11533
|
+
}
|
|
11534
|
+
}
|
|
11535
|
+
return tokens;
|
|
11536
|
+
}
|
|
11537
|
+
function generateDefaultTokens() {
|
|
11538
|
+
return {
|
|
11539
|
+
colors: {
|
|
11540
|
+
"brand-50": "#f0f7ff",
|
|
11541
|
+
"brand-100": "#e0efff",
|
|
11542
|
+
"brand-200": "#b8d9ff",
|
|
11543
|
+
"brand-500": "#0066cc",
|
|
11544
|
+
"brand-600": "#0052a3",
|
|
11545
|
+
"brand-700": "#003d7a",
|
|
11546
|
+
"brand-900": "#001f3f",
|
|
11547
|
+
"gray-50": "#f9fafb",
|
|
11548
|
+
"gray-100": "#f3f4f6",
|
|
11549
|
+
"gray-200": "#e5e7eb",
|
|
11550
|
+
"gray-500": "#6b7280",
|
|
11551
|
+
"gray-700": "#374151",
|
|
11552
|
+
"gray-900": "#111827",
|
|
11553
|
+
"success": "#22c55e",
|
|
11554
|
+
"warning": "#f59e0b",
|
|
11555
|
+
"error": "#ef4444"
|
|
11556
|
+
},
|
|
11557
|
+
typography: {
|
|
11558
|
+
fontFamilies: ['-apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif'],
|
|
11559
|
+
scale: {
|
|
11560
|
+
"xs": "0.75rem",
|
|
11561
|
+
"sm": "0.875rem",
|
|
11562
|
+
"base": "1rem",
|
|
11563
|
+
"lg": "1.125rem",
|
|
11564
|
+
"xl": "1.25rem",
|
|
11565
|
+
"2xl": "1.5rem",
|
|
11566
|
+
"3xl": "1.875rem",
|
|
11567
|
+
"4xl": "2.25rem"
|
|
11568
|
+
}
|
|
11569
|
+
},
|
|
11570
|
+
spacing: {
|
|
11571
|
+
"xs": "0.25rem",
|
|
11572
|
+
"sm": "0.5rem",
|
|
11573
|
+
"md": "1rem",
|
|
11574
|
+
"lg": "1.5rem",
|
|
11575
|
+
"xl": "2rem",
|
|
11576
|
+
"2xl": "3rem",
|
|
11577
|
+
"3xl": "4rem"
|
|
11578
|
+
},
|
|
11579
|
+
borderRadius: {
|
|
11580
|
+
"sm": "0.25rem",
|
|
11581
|
+
"md": "0.375rem",
|
|
11582
|
+
"lg": "0.5rem",
|
|
11583
|
+
"xl": "0.75rem",
|
|
11584
|
+
"full": "9999px"
|
|
11585
|
+
},
|
|
11586
|
+
shadows: {
|
|
11587
|
+
"sm": "0 1px 2px rgba(0,0,0,0.05)",
|
|
11588
|
+
"md": "0 4px 6px rgba(0,0,0,0.1)",
|
|
11589
|
+
"lg": "0 10px 15px rgba(0,0,0,0.1)"
|
|
11590
|
+
},
|
|
11591
|
+
darkMode: true
|
|
11592
|
+
};
|
|
11593
|
+
}
|
|
11594
|
+
function detectComponentRegistry(projectDir) {
|
|
11595
|
+
const registry = {
|
|
11596
|
+
components: [],
|
|
11597
|
+
library: null,
|
|
11598
|
+
framework: null
|
|
11599
|
+
};
|
|
11600
|
+
const pkgPath = join7(projectDir, "package.json");
|
|
11601
|
+
if (existsSync7(pkgPath)) {
|
|
11602
|
+
const pkg = JSON.parse(readFileSync4(pkgPath, "utf-8"));
|
|
11603
|
+
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
11604
|
+
if (deps["@shadcn/ui"] || existsSync7(join7(projectDir, "components.json"))) registry.library = "shadcn";
|
|
11605
|
+
else if (deps["@radix-ui/react-dialog"] || deps["@radix-ui/themes"]) registry.library = "radix";
|
|
11606
|
+
else if (deps["@mui/material"]) registry.library = "mui";
|
|
11607
|
+
else if (deps["antd"]) registry.library = "antd";
|
|
11608
|
+
else if (deps["@chakra-ui/react"]) registry.library = "chakra";
|
|
11609
|
+
else if (deps["@headlessui/react"]) registry.library = "headlessui";
|
|
11610
|
+
if (deps["react"] || deps["react-dom"]) registry.framework = "react";
|
|
11611
|
+
else if (deps["vue"]) registry.framework = "vue";
|
|
11612
|
+
else if (deps["svelte"]) registry.framework = "svelte";
|
|
11613
|
+
else if (deps["@angular/core"]) registry.framework = "angular";
|
|
11614
|
+
}
|
|
11615
|
+
const componentDirs = ["src/components", "components", "src/ui", "app/components", "src/components/ui"];
|
|
11616
|
+
for (const dir of componentDirs) {
|
|
11617
|
+
const fullDir = join7(projectDir, dir);
|
|
11618
|
+
if (existsSync7(fullDir)) {
|
|
11619
|
+
try {
|
|
11620
|
+
scanComponents(fullDir, dir, registry.components);
|
|
11621
|
+
} catch {
|
|
11622
|
+
}
|
|
11623
|
+
}
|
|
11624
|
+
}
|
|
11625
|
+
return registry;
|
|
11626
|
+
}
|
|
11627
|
+
function scanComponents(dir, relBase, components) {
|
|
11628
|
+
try {
|
|
11629
|
+
const entries = readdirSync(dir, { withFileTypes: true });
|
|
11630
|
+
for (const entry of entries) {
|
|
11631
|
+
if (entry.isDirectory()) {
|
|
11632
|
+
scanComponents(join7(dir, entry.name), `${relBase}/${entry.name}`, components);
|
|
11633
|
+
} else if (entry.name.match(/\.(tsx|jsx|vue|svelte)$/)) {
|
|
11634
|
+
const name = entry.name.replace(/\.(tsx|jsx|vue|svelte)$/, "");
|
|
11635
|
+
if (name === "index") continue;
|
|
11636
|
+
const type = classifyComponent(name);
|
|
11637
|
+
components.push({ name, path: `${relBase}/${entry.name}`, type });
|
|
11638
|
+
}
|
|
11639
|
+
}
|
|
11640
|
+
} catch {
|
|
11641
|
+
}
|
|
11642
|
+
}
|
|
11643
|
+
function classifyComponent(name) {
|
|
11644
|
+
const lower = name.toLowerCase();
|
|
11645
|
+
if (/button|badge|avatar|icon|tag|chip|tooltip/i.test(lower)) return "ui";
|
|
11646
|
+
if (/layout|header|footer|sidebar|menu/i.test(lower)) return "layout";
|
|
11647
|
+
if (/input|form|select|checkbox|radio|textarea|switch|slider/i.test(lower)) return "form";
|
|
11648
|
+
if (/table|list|card|grid|chart|graph|stat/i.test(lower)) return "data";
|
|
11649
|
+
if (/link|breadcrumb|tab|pagination|stepper|nav|navbar/i.test(lower)) return "navigation";
|
|
11650
|
+
return "other";
|
|
11651
|
+
}
|
|
11652
|
+
function runBrandGuardian(projectDir, tokens) {
|
|
11653
|
+
const issues = [];
|
|
11654
|
+
let filesScanned = 0;
|
|
11655
|
+
const files = findFiles(projectDir, ["src", "app", "pages", "components"], [".tsx", ".jsx", ".vue", ".svelte", ".css"]);
|
|
11656
|
+
for (const file of files.slice(0, 100)) {
|
|
11657
|
+
try {
|
|
11658
|
+
const content = readFileSync4(file, "utf-8");
|
|
11659
|
+
const lines = content.split("\n");
|
|
11660
|
+
const relPath = file.replace(projectDir, "").replace(/\\/g, "/").replace(/^\//, "");
|
|
11661
|
+
filesScanned++;
|
|
11662
|
+
for (let i = 0; i < lines.length; i++) {
|
|
11663
|
+
const line = lines[i];
|
|
11664
|
+
const lineNum = i + 1;
|
|
11665
|
+
if (file.match(/\.(tsx|jsx|vue|svelte)$/)) {
|
|
11666
|
+
const hexMatches = line.matchAll(/#[0-9a-fA-F]{3,8}\b/g);
|
|
11667
|
+
for (const m of hexMatches) {
|
|
11668
|
+
if (line.trimStart().startsWith("//") || line.trimStart().startsWith("*")) continue;
|
|
11669
|
+
if (line.includes("--")) continue;
|
|
11670
|
+
issues.push({
|
|
11671
|
+
file: relPath,
|
|
11672
|
+
line: lineNum,
|
|
11673
|
+
severity: "warning",
|
|
11674
|
+
rule: "no-hardcoded-colors",
|
|
11675
|
+
message: `Hardcoded color ${m[0]} \u2014 use design tokens instead`,
|
|
11676
|
+
fix: "Replace with a CSS variable or Tailwind class from your design system"
|
|
11677
|
+
});
|
|
11678
|
+
}
|
|
11679
|
+
}
|
|
11680
|
+
if (line.includes("style=") && line.match(/\d+px/)) {
|
|
11681
|
+
issues.push({
|
|
11682
|
+
file: relPath,
|
|
11683
|
+
line: lineNum,
|
|
11684
|
+
severity: "warning",
|
|
11685
|
+
rule: "no-inline-px",
|
|
11686
|
+
message: "Inline px values \u2014 use spacing tokens or Tailwind classes",
|
|
11687
|
+
fix: "Replace px values with spacing scale (xs, sm, md, lg, xl)"
|
|
11688
|
+
});
|
|
11689
|
+
}
|
|
11690
|
+
if (line.match(/<img\b/) && !line.includes("alt=") && !line.includes("alt =")) {
|
|
11691
|
+
issues.push({
|
|
11692
|
+
file: relPath,
|
|
11693
|
+
line: lineNum,
|
|
11694
|
+
severity: "error",
|
|
11695
|
+
rule: "img-alt-text",
|
|
11696
|
+
message: "Image missing alt attribute \u2014 accessibility violation",
|
|
11697
|
+
fix: 'Add alt="descriptive text" or alt="" for decorative images'
|
|
11698
|
+
});
|
|
11699
|
+
}
|
|
11700
|
+
if (line.includes("onClick") && !line.includes("onKeyDown") && !line.includes("onKeyUp") && !line.includes("onKeyPress")) {
|
|
11701
|
+
if (line.includes("<div") || line.includes("<span")) {
|
|
11702
|
+
issues.push({
|
|
11703
|
+
file: relPath,
|
|
11704
|
+
line: lineNum,
|
|
11705
|
+
severity: "warning",
|
|
11706
|
+
rule: "keyboard-accessible",
|
|
11707
|
+
message: "Click handler on non-interactive element without keyboard support",
|
|
11708
|
+
fix: 'Add onKeyDown handler and role="button" tabIndex={0}, or use <button>'
|
|
11709
|
+
});
|
|
11710
|
+
}
|
|
11711
|
+
}
|
|
11712
|
+
if (line.match(/font-size:\s*\d+px/) && !line.includes("--")) {
|
|
11713
|
+
issues.push({
|
|
11714
|
+
file: relPath,
|
|
11715
|
+
line: lineNum,
|
|
11716
|
+
severity: "info",
|
|
11717
|
+
rule: "use-typography-scale",
|
|
11718
|
+
message: "Hardcoded font-size \u2014 use typography scale tokens",
|
|
11719
|
+
fix: "Replace with typography scale class (text-xs, text-sm, text-base, etc.)"
|
|
11720
|
+
});
|
|
11721
|
+
}
|
|
11722
|
+
if (line.match(/<html\b/) && !line.includes("lang=")) {
|
|
11723
|
+
issues.push({
|
|
11724
|
+
file: relPath,
|
|
11725
|
+
line: lineNum,
|
|
11726
|
+
severity: "error",
|
|
11727
|
+
rule: "html-lang",
|
|
11728
|
+
message: "HTML element missing lang attribute \u2014 accessibility violation",
|
|
11729
|
+
fix: 'Add lang="en" (or appropriate language code) to <html>'
|
|
11730
|
+
});
|
|
11731
|
+
}
|
|
11732
|
+
}
|
|
11733
|
+
} catch {
|
|
11734
|
+
}
|
|
11735
|
+
}
|
|
11736
|
+
const errors = issues.filter((i) => i.severity === "error").length;
|
|
11737
|
+
const warnings = issues.filter((i) => i.severity === "warning").length;
|
|
11738
|
+
const infos = issues.filter((i) => i.severity === "info").length;
|
|
11739
|
+
return {
|
|
11740
|
+
issues,
|
|
11741
|
+
passed: errors === 0,
|
|
11742
|
+
stats: { errors, warnings, infos, filesScanned }
|
|
11743
|
+
};
|
|
11744
|
+
}
|
|
11745
|
+
function generateDesignSystemConfig(tokens, registry) {
|
|
11746
|
+
return JSON.stringify({
|
|
11747
|
+
version: "1.0",
|
|
11748
|
+
tokens,
|
|
11749
|
+
components: {
|
|
11750
|
+
library: registry.library,
|
|
11751
|
+
framework: registry.framework,
|
|
11752
|
+
count: registry.components.length,
|
|
11753
|
+
entries: registry.components
|
|
11754
|
+
}
|
|
11755
|
+
}, null, 2) + "\n";
|
|
11756
|
+
}
|
|
11757
|
+
function findFiles(base, dirs, extensions) {
|
|
11758
|
+
const files = [];
|
|
11759
|
+
for (const dir of dirs) {
|
|
11760
|
+
const fullDir = join7(base, dir);
|
|
11761
|
+
if (existsSync7(fullDir)) {
|
|
11762
|
+
walkDir(fullDir, extensions, files);
|
|
11763
|
+
}
|
|
11764
|
+
}
|
|
11765
|
+
return files;
|
|
11766
|
+
}
|
|
11767
|
+
function walkDir(dir, extensions, result) {
|
|
11768
|
+
try {
|
|
11769
|
+
const entries = readdirSync(dir, { withFileTypes: true });
|
|
11770
|
+
for (const entry of entries) {
|
|
11771
|
+
const full = join7(dir, entry.name);
|
|
11772
|
+
if (entry.isDirectory()) {
|
|
11773
|
+
if (entry.name === "node_modules" || entry.name === ".next" || entry.name === "dist" || entry.name === ".git") continue;
|
|
11774
|
+
walkDir(full, extensions, result);
|
|
11775
|
+
} else if (extensions.some((ext) => entry.name.endsWith(ext))) {
|
|
11776
|
+
result.push(full);
|
|
11777
|
+
}
|
|
11778
|
+
}
|
|
11779
|
+
} catch {
|
|
11780
|
+
}
|
|
11781
|
+
}
|
|
11782
|
+
|
|
11371
11783
|
// src/templates/configs/sandbox-policy.ts
|
|
11372
11784
|
function getSandboxPolicyTemplate() {
|
|
11373
11785
|
return `# FISHI Sandbox Policy
|
|
@@ -11764,7 +12176,10 @@ export {
|
|
|
11764
12176
|
backendAgentTemplate,
|
|
11765
12177
|
buildSandboxEnv,
|
|
11766
12178
|
createBackup,
|
|
12179
|
+
detectComponentRegistry,
|
|
11767
12180
|
detectConflicts,
|
|
12181
|
+
detectDesignTokens,
|
|
12182
|
+
detectDevServer,
|
|
11768
12183
|
detectDocker,
|
|
11769
12184
|
devLeadTemplate,
|
|
11770
12185
|
devopsAgentTemplate,
|
|
@@ -11772,6 +12187,8 @@ export {
|
|
|
11772
12187
|
emitEvent,
|
|
11773
12188
|
frontendAgentTemplate,
|
|
11774
12189
|
fullstackAgentTemplate,
|
|
12190
|
+
generateDefaultTokens,
|
|
12191
|
+
generateDesignSystemConfig,
|
|
11775
12192
|
generateScaffold,
|
|
11776
12193
|
getAdaptiveTaskGraphSkill,
|
|
11777
12194
|
getAgentCompleteHook,
|
|
@@ -11822,6 +12239,7 @@ export {
|
|
|
11822
12239
|
getTestingSkill,
|
|
11823
12240
|
getTodoManagerScript,
|
|
11824
12241
|
getValidateScaffoldScript,
|
|
12242
|
+
getVibeModeConfig,
|
|
11825
12243
|
getWorktreeManagerScript,
|
|
11826
12244
|
getWorktreeSetupHook,
|
|
11827
12245
|
marketingAgentTemplate,
|
|
@@ -11838,10 +12256,12 @@ export {
|
|
|
11838
12256
|
readSandboxConfig,
|
|
11839
12257
|
readSandboxPolicy,
|
|
11840
12258
|
researchAgentTemplate,
|
|
12259
|
+
runBrandGuardian,
|
|
11841
12260
|
runInDockerSandbox,
|
|
11842
12261
|
runInProcessSandbox,
|
|
11843
12262
|
runInSandbox,
|
|
11844
12263
|
securityAgentTemplate,
|
|
12264
|
+
startDevServer,
|
|
11845
12265
|
testingAgentTemplate,
|
|
11846
12266
|
uiuxAgentTemplate,
|
|
11847
12267
|
writingAgentTemplate
|