@qlucent/fishi-core 0.6.0 → 0.8.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 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';
@@ -568,6 +570,83 @@ declare function getAgentSummary(projectDir: string): Record<string, {
568
570
  filesChanged: number;
569
571
  }>;
570
572
 
573
+ type SandboxMode = 'docker' | 'process';
574
+ interface SandboxConfig {
575
+ mode: SandboxMode;
576
+ dockerAvailable: boolean;
577
+ }
578
+ interface SandboxPolicy {
579
+ networkAllow: string[];
580
+ envPassthrough: string[];
581
+ timeout: number;
582
+ memory: string;
583
+ cpus: number;
584
+ }
585
+ interface SandboxRunResult {
586
+ stdout: string;
587
+ stderr: string;
588
+ exitCode: number;
589
+ timedOut: boolean;
590
+ }
591
+ /**
592
+ * Detect if Docker is installed and running.
593
+ */
594
+ declare function detectDocker(): boolean;
595
+ /**
596
+ * Read sandbox config from .fishi/fishi.yaml
597
+ */
598
+ declare function readSandboxConfig(projectDir: string): SandboxConfig;
599
+ /**
600
+ * Read sandbox policy from .fishi/sandbox-policy.yaml, or return defaults.
601
+ */
602
+ declare function readSandboxPolicy(projectDir: string): SandboxPolicy;
603
+ /**
604
+ * Build a restricted env object for process mode.
605
+ * Strips all env vars except explicitly allowed ones + essentials.
606
+ */
607
+ declare function buildSandboxEnv(policy: SandboxPolicy): Record<string, string>;
608
+ /**
609
+ * Run a command in process-mode sandbox.
610
+ */
611
+ declare function runInProcessSandbox(command: string, args: string[], worktreePath: string, policy: SandboxPolicy): SandboxRunResult;
612
+ /**
613
+ * Run a command in Docker sandbox.
614
+ */
615
+ declare function runInDockerSandbox(command: string, args: string[], worktreePath: string, policy: SandboxPolicy, options?: {
616
+ nodeModulesPath?: string;
617
+ }): SandboxRunResult;
618
+ /**
619
+ * Run a command in the configured sandbox mode.
620
+ */
621
+ declare function runInSandbox(command: string, args: string[], worktreePath: string, projectDir: string, options?: {
622
+ nodeModulesPath?: string;
623
+ }): SandboxRunResult;
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
+ declare function getSandboxPolicyTemplate(): string;
647
+
648
+ declare function getDockerfileTemplate(): string;
649
+
571
650
  declare function getDashboardHtml(): string;
572
651
 
573
- 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 ScaffoldOptions, type ScaffoldResult, type SkillTemplate, type StateConfig, type TaskStatus, type TaskboardConfig, type TemplateContext, architectAgentTemplate, backendAgentTemplate, createBackup, detectConflicts, 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, getDocumentationSkill, getFishiYamlTemplate, getGateCommand, getGateManagerScript, getGitignoreAdditions, getInitCommand, getLearningsManagerScript, getMasterOrchestratorTemplate, getMcpJsonTemplate, getMemoryManagerScript, getModelRoutingReference, getMonitorEmitterScript, getPhaseRunnerScript, getPostEditHook, getPrdCommand, getPrdSkill, getProjectYamlTemplate, getResetCommand, getResumeCommand, getSafetyCheckHook, getSessionStartHook, getSettingsJsonTemplate, getSprintCommand, getStatusCommand, getTaskboardOpsSkill, getTaskboardUpdateHook, getTestingSkill, getTodoManagerScript, getValidateScaffoldScript, getWorktreeManagerScript, getWorktreeSetupHook, marketingAgentTemplate, mergeClaudeMd, mergeClaudeMdTop, mergeGitignore, mergeMcpJson, mergeSettingsJson, opsLeadTemplate, planningAgentTemplate, planningLeadTemplate, qualityLeadTemplate, readMonitorState, researchAgentTemplate, securityAgentTemplate, testingAgentTemplate, uiuxAgentTemplate, writingAgentTemplate };
652
+ 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 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, detectConflicts, detectDevServer, 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, getVibeModeConfig, getWorktreeManagerScript, getWorktreeSetupHook, marketingAgentTemplate, mergeClaudeMd, mergeClaudeMdTop, mergeGitignore, mergeMcpJson, mergeSettingsJson, opsLeadTemplate, planningAgentTemplate, planningLeadTemplate, qualityLeadTemplate, readMonitorState, readSandboxConfig, readSandboxPolicy, researchAgentTemplate, 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.6.0";
11114
+ const fishiVersion = "0.8.0";
11115
11115
  const manifest = {
11116
11116
  timestamp: now.toISOString(),
11117
11117
  fishi_version: fishiVersion,
@@ -11218,6 +11218,302 @@ function getAgentSummary(projectDir) {
11218
11218
  return agents;
11219
11219
  }
11220
11220
 
11221
+ // src/generators/sandbox.ts
11222
+ import { execSync, execFileSync } from "child_process";
11223
+ import { existsSync as existsSync5, readFileSync as readFileSync2 } from "fs";
11224
+ import { join as join5 } from "path";
11225
+ var DEFAULT_POLICY = {
11226
+ networkAllow: ["registry.npmjs.org", "localhost", "127.0.0.1"],
11227
+ envPassthrough: [],
11228
+ timeout: 600,
11229
+ // 10 minutes
11230
+ memory: "2g",
11231
+ cpus: 2
11232
+ };
11233
+ function detectDocker() {
11234
+ try {
11235
+ execSync("docker info", { stdio: "pipe", timeout: 5e3 });
11236
+ return true;
11237
+ } catch {
11238
+ return false;
11239
+ }
11240
+ }
11241
+ function readSandboxConfig(projectDir) {
11242
+ const yamlPath = join5(projectDir, ".fishi", "fishi.yaml");
11243
+ if (!existsSync5(yamlPath)) {
11244
+ return { mode: "process", dockerAvailable: false };
11245
+ }
11246
+ const content = readFileSync2(yamlPath, "utf-8");
11247
+ const modeMatch = content.match(/^\s*mode:\s*(docker|process)/m);
11248
+ const dockerMatch = content.match(/^\s*docker_available:\s*(true|false)/m);
11249
+ return {
11250
+ mode: modeMatch?.[1] || "process",
11251
+ dockerAvailable: dockerMatch?.[1] === "true"
11252
+ };
11253
+ }
11254
+ function readSandboxPolicy(projectDir) {
11255
+ const policyPath = join5(projectDir, ".fishi", "sandbox-policy.yaml");
11256
+ if (!existsSync5(policyPath)) return { ...DEFAULT_POLICY };
11257
+ const content = readFileSync2(policyPath, "utf-8");
11258
+ const networkAllow = extractYamlList(content, "network_allow") || DEFAULT_POLICY.networkAllow;
11259
+ const envPassthrough = extractYamlList(content, "env_passthrough") || DEFAULT_POLICY.envPassthrough;
11260
+ const timeoutMatch = content.match(/^\s*timeout:\s*(\d+)/m);
11261
+ const memoryMatch = content.match(/^\s*memory:\s*["']?(\S+?)["']?\s*$/m);
11262
+ const cpusMatch = content.match(/^\s*cpus:\s*(\d+)/m);
11263
+ return {
11264
+ networkAllow,
11265
+ envPassthrough,
11266
+ timeout: timeoutMatch ? parseInt(timeoutMatch[1], 10) : DEFAULT_POLICY.timeout,
11267
+ memory: memoryMatch?.[1] || DEFAULT_POLICY.memory,
11268
+ cpus: cpusMatch ? parseInt(cpusMatch[1], 10) : DEFAULT_POLICY.cpus
11269
+ };
11270
+ }
11271
+ function extractYamlList(content, key) {
11272
+ const regex = new RegExp(`^\\s*${key}:\\s*\\n((?:\\s+-\\s*.+\\n?)*)`, "m");
11273
+ const match = content.match(regex);
11274
+ if (!match) return null;
11275
+ return match[1].split("\n").map((line) => {
11276
+ const m = line.match(/^\s*-\s*["']?(.+?)["']?\s*$/);
11277
+ return m ? m[1] : "";
11278
+ }).filter(Boolean);
11279
+ }
11280
+ function buildSandboxEnv(policy) {
11281
+ const env = {
11282
+ PATH: process.env.PATH || "",
11283
+ HOME: process.env.HOME || process.env.USERPROFILE || "",
11284
+ NODE_ENV: "development",
11285
+ FISHI_SANDBOX: "true"
11286
+ };
11287
+ for (const key of policy.envPassthrough) {
11288
+ if (process.env[key]) {
11289
+ env[key] = process.env[key];
11290
+ }
11291
+ }
11292
+ return env;
11293
+ }
11294
+ function runInProcessSandbox(command, args, worktreePath, policy) {
11295
+ const env = buildSandboxEnv(policy);
11296
+ try {
11297
+ const stdout = execFileSync(command, args, {
11298
+ cwd: worktreePath,
11299
+ encoding: "utf-8",
11300
+ timeout: policy.timeout * 1e3,
11301
+ env,
11302
+ stdio: "pipe",
11303
+ maxBuffer: 10 * 1024 * 1024
11304
+ // 10MB
11305
+ });
11306
+ return { stdout, stderr: "", exitCode: 0, timedOut: false };
11307
+ } catch (e) {
11308
+ if (e.killed || e.signal === "SIGTERM") {
11309
+ return { stdout: e.stdout || "", stderr: e.stderr || "", exitCode: 1, timedOut: true };
11310
+ }
11311
+ return {
11312
+ stdout: e.stdout || "",
11313
+ stderr: e.stderr || "",
11314
+ exitCode: e.status ?? 1,
11315
+ timedOut: false
11316
+ };
11317
+ }
11318
+ }
11319
+ function runInDockerSandbox(command, args, worktreePath, policy, options = {}) {
11320
+ const dockerArgs = [
11321
+ "run",
11322
+ "--rm",
11323
+ "--workdir",
11324
+ "/workspace",
11325
+ "-v",
11326
+ `${worktreePath}:/workspace`
11327
+ ];
11328
+ if (options.nodeModulesPath && existsSync5(options.nodeModulesPath)) {
11329
+ dockerArgs.push("-v", `${options.nodeModulesPath}:/workspace/node_modules:ro`);
11330
+ }
11331
+ dockerArgs.push("--memory", policy.memory);
11332
+ dockerArgs.push("--cpus", String(policy.cpus));
11333
+ dockerArgs.push("-e", "FISHI_SANDBOX=true");
11334
+ dockerArgs.push("-e", "NODE_ENV=development");
11335
+ for (const key of policy.envPassthrough) {
11336
+ if (process.env[key]) {
11337
+ dockerArgs.push("-e", `${key}=${process.env[key]}`);
11338
+ }
11339
+ }
11340
+ dockerArgs.push("--network", "host");
11341
+ dockerArgs.push("fishi-sandbox:latest", command, ...args);
11342
+ try {
11343
+ const stdout = execFileSync("docker", dockerArgs, {
11344
+ encoding: "utf-8",
11345
+ timeout: policy.timeout * 1e3,
11346
+ stdio: "pipe",
11347
+ maxBuffer: 10 * 1024 * 1024
11348
+ });
11349
+ return { stdout, stderr: "", exitCode: 0, timedOut: false };
11350
+ } catch (e) {
11351
+ if (e.killed || e.signal === "SIGTERM") {
11352
+ return { stdout: e.stdout || "", stderr: e.stderr || "", exitCode: 1, timedOut: true };
11353
+ }
11354
+ return {
11355
+ stdout: e.stdout || "",
11356
+ stderr: e.stderr || "",
11357
+ exitCode: e.status ?? 1,
11358
+ timedOut: false
11359
+ };
11360
+ }
11361
+ }
11362
+ function runInSandbox(command, args, worktreePath, projectDir, options = {}) {
11363
+ const config = readSandboxConfig(projectDir);
11364
+ const policy = readSandboxPolicy(projectDir);
11365
+ if (config.mode === "docker" && config.dockerAvailable) {
11366
+ return runInDockerSandbox(command, args, worktreePath, policy, options);
11367
+ }
11368
+ return runInProcessSandbox(command, args, worktreePath, policy);
11369
+ }
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/templates/configs/sandbox-policy.ts
11470
+ function getSandboxPolicyTemplate() {
11471
+ return `# FISHI Sandbox Policy
11472
+ # Controls what agents can access inside their sandboxed worktrees
11473
+
11474
+ # Network domains agents are allowed to reach
11475
+ network_allow:
11476
+ - registry.npmjs.org
11477
+ - localhost
11478
+ - 127.0.0.1
11479
+
11480
+ # Environment variables passed into the sandbox
11481
+ # Add secrets your agents need (e.g., DATABASE_URL, API_KEY)
11482
+ env_passthrough: []
11483
+
11484
+ # Maximum time (seconds) a single agent command can run
11485
+ timeout: 600
11486
+
11487
+ # Docker resource limits (only applies in docker mode)
11488
+ memory: "2g"
11489
+ cpus: 2
11490
+ `;
11491
+ }
11492
+
11493
+ // src/templates/docker/Dockerfile.ts
11494
+ function getDockerfileTemplate() {
11495
+ return `# FISHI Sandbox Runtime
11496
+ # Minimal Node.js image for agent execution
11497
+ FROM node:22-slim
11498
+
11499
+ # Install git and common build tools
11500
+ RUN apt-get update && apt-get install -y --no-install-recommends \\
11501
+ git \\
11502
+ ca-certificates \\
11503
+ && rm -rf /var/lib/apt/lists/*
11504
+
11505
+ # Set working directory
11506
+ WORKDIR /workspace
11507
+
11508
+ # Non-root user for additional safety
11509
+ RUN groupadd -r fishi && useradd -r -g fishi -m fishi
11510
+ USER fishi
11511
+
11512
+ # Default command
11513
+ CMD ["node"]
11514
+ `;
11515
+ }
11516
+
11221
11517
  // src/templates/dashboard/index-html.ts
11222
11518
  function getDashboardHtml() {
11223
11519
  return `<!DOCTYPE html>
@@ -11564,8 +11860,11 @@ function getDashboardHtml() {
11564
11860
  export {
11565
11861
  architectAgentTemplate,
11566
11862
  backendAgentTemplate,
11863
+ buildSandboxEnv,
11567
11864
  createBackup,
11568
11865
  detectConflicts,
11866
+ detectDevServer,
11867
+ detectDocker,
11569
11868
  devLeadTemplate,
11570
11869
  devopsAgentTemplate,
11571
11870
  docsAgentTemplate,
@@ -11591,6 +11890,7 @@ export {
11591
11890
  getDebuggingSkill,
11592
11891
  getDeploymentSkill,
11593
11892
  getDocCheckerScript,
11893
+ getDockerfileTemplate,
11594
11894
  getDocumentationSkill,
11595
11895
  getFishiYamlTemplate,
11596
11896
  getGateCommand,
@@ -11611,6 +11911,7 @@ export {
11611
11911
  getResetCommand,
11612
11912
  getResumeCommand,
11613
11913
  getSafetyCheckHook,
11914
+ getSandboxPolicyTemplate,
11614
11915
  getSessionStartHook,
11615
11916
  getSettingsJsonTemplate,
11616
11917
  getSprintCommand,
@@ -11620,6 +11921,7 @@ export {
11620
11921
  getTestingSkill,
11621
11922
  getTodoManagerScript,
11622
11923
  getValidateScaffoldScript,
11924
+ getVibeModeConfig,
11623
11925
  getWorktreeManagerScript,
11624
11926
  getWorktreeSetupHook,
11625
11927
  marketingAgentTemplate,
@@ -11633,8 +11935,14 @@ export {
11633
11935
  planningLeadTemplate,
11634
11936
  qualityLeadTemplate,
11635
11937
  readMonitorState,
11938
+ readSandboxConfig,
11939
+ readSandboxPolicy,
11636
11940
  researchAgentTemplate,
11941
+ runInDockerSandbox,
11942
+ runInProcessSandbox,
11943
+ runInSandbox,
11637
11944
  securityAgentTemplate,
11945
+ startDevServer,
11638
11946
  testingAgentTemplate,
11639
11947
  uiuxAgentTemplate,
11640
11948
  writingAgentTemplate
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@qlucent/fishi-core",
3
- "version": "0.6.0",
3
+ "version": "0.8.0",
4
4
  "description": "Shared templates, types, and generators for the FISHI framework",
5
5
  "license": "MIT",
6
6
  "type": "module",