@qlucent/fishi 0.9.0 → 0.12.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.
Files changed (2) hide show
  1. package/dist/index.js +109 -5
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  // src/index.ts
4
4
  import { Command } from "commander";
5
- import chalk12 from "chalk";
5
+ import chalk13 from "chalk";
6
6
 
7
7
  // src/commands/init.ts
8
8
  import chalk from "chalk";
@@ -10,7 +10,7 @@ import ora from "ora";
10
10
  import inquirer from "inquirer";
11
11
  import path3 from "path";
12
12
  import fs3 from "fs";
13
- import { detectConflicts, createBackup, detectDocker } from "@qlucent/fishi-core";
13
+ import { detectConflicts, createBackup, detectDocker, getAvailableDomains, getDomainConfigYaml } from "@qlucent/fishi-core";
14
14
 
15
15
  // src/analyzers/detector.ts
16
16
  import fs from "fs";
@@ -1079,6 +1079,18 @@ async function initCommand(description, options) {
1079
1079
  }
1080
1080
  }
1081
1081
  }
1082
+ let selectedDomain = "general";
1083
+ if (options.interactive !== false) {
1084
+ const domains = getAvailableDomains();
1085
+ const { domain } = await inquirer.prompt([{
1086
+ type: "list",
1087
+ name: "domain",
1088
+ message: "What type of application are you building?",
1089
+ choices: domains,
1090
+ default: "general"
1091
+ }]);
1092
+ selectedDomain = domain;
1093
+ }
1082
1094
  const conflictResult = detectConflicts(targetDir);
1083
1095
  let resolutions;
1084
1096
  if (conflictResult.hasConflicts) {
@@ -1154,7 +1166,8 @@ async function initCommand(description, options) {
1154
1166
  brownfieldAnalysis: brownfieldData,
1155
1167
  resolutions,
1156
1168
  docsReadmeExists: conflictResult?.docsReadmeExists,
1157
- rootClaudeMdExists: conflictResult?.rootClaudeMdExists
1169
+ rootClaudeMdExists: conflictResult?.rootClaudeMdExists,
1170
+ domain: selectedDomain
1158
1171
  });
1159
1172
  if (brownfieldAnalysis) {
1160
1173
  const reportPath = path3.join(targetDir, ".fishi", "memory", "brownfield-analysis.md");
@@ -1174,6 +1187,12 @@ sandbox:
1174
1187
  if (fs3.existsSync(fishiYamlPath)) {
1175
1188
  fs3.appendFileSync(fishiYamlPath, sandboxYaml, "utf-8");
1176
1189
  }
1190
+ if (selectedDomain !== "general") {
1191
+ const fishiYamlPath2 = path3.join(targetDir, ".fishi", "fishi.yaml");
1192
+ if (fs3.existsSync(fishiYamlPath2)) {
1193
+ fs3.appendFileSync(fishiYamlPath2, getDomainConfigYaml(selectedDomain), "utf-8");
1194
+ }
1195
+ }
1177
1196
  const { getSandboxPolicyTemplate, getDockerfileTemplate } = await import("@qlucent/fishi-core");
1178
1197
  fs3.writeFileSync(path3.join(targetDir, ".fishi", "sandbox-policy.yaml"), getSandboxPolicyTemplate(), "utf-8");
1179
1198
  if (sandboxMode === "docker") {
@@ -2256,11 +2275,95 @@ async function designCommand(action, options) {
2256
2275
  }
2257
2276
  }
2258
2277
 
2278
+ // src/commands/security.ts
2279
+ import chalk12 from "chalk";
2280
+ import ora3 from "ora";
2281
+ import fs14 from "fs";
2282
+ import path14 from "path";
2283
+ import { runSecurityScan, generateSecurityReport, getScanRules } from "@qlucent/fishi-core";
2284
+ async function securityCommand(action, options) {
2285
+ const targetDir = process.cwd();
2286
+ if (action === "scan") {
2287
+ console.log("");
2288
+ console.log(chalk12.cyan.bold(" FISHI Security Scanner"));
2289
+ console.log(chalk12.gray(" Native SAST + OWASP vulnerability detection"));
2290
+ console.log("");
2291
+ const spinner = ora3("Scanning for vulnerabilities...").start();
2292
+ const report = runSecurityScan(targetDir);
2293
+ spinner.succeed(`Scanned ${report.summary.filesScanned} files`);
2294
+ console.log("");
2295
+ if (options.json) {
2296
+ console.log(JSON.stringify(report, null, 2));
2297
+ } else {
2298
+ const severityOrder = ["critical", "high", "medium", "low", "info"];
2299
+ const severityColors = {
2300
+ critical: chalk12.red.bold,
2301
+ high: chalk12.red,
2302
+ medium: chalk12.yellow,
2303
+ low: chalk12.blue,
2304
+ info: chalk12.gray
2305
+ };
2306
+ for (const sev of severityOrder) {
2307
+ const sevFindings = report.findings.filter((f) => f.severity === sev);
2308
+ if (sevFindings.length === 0) continue;
2309
+ console.log(severityColors[sev](` ${sev.toUpperCase()} (${sevFindings.length})`));
2310
+ for (const f of sevFindings) {
2311
+ console.log(chalk12.gray(` ${f.file}:${f.line}`));
2312
+ console.log(` ${f.message}`);
2313
+ if (f.cwe) console.log(chalk12.gray(` ${f.cwe}`));
2314
+ console.log(chalk12.green(` Fix: ${f.fix}`));
2315
+ console.log("");
2316
+ }
2317
+ }
2318
+ console.log(chalk12.white.bold(" Summary"));
2319
+ if (report.summary.critical > 0) console.log(chalk12.red(` Critical: ${report.summary.critical}`));
2320
+ if (report.summary.high > 0) console.log(chalk12.red(` High: ${report.summary.high}`));
2321
+ if (report.summary.medium > 0) console.log(chalk12.yellow(` Medium: ${report.summary.medium}`));
2322
+ if (report.summary.low > 0) console.log(chalk12.blue(` Low: ${report.summary.low}`));
2323
+ if (report.summary.info > 0) console.log(chalk12.gray(` Info: ${report.summary.info}`));
2324
+ console.log(` Total: ${report.summary.total}`);
2325
+ console.log(` Status: ${report.summary.passed ? chalk12.green("PASSED") : chalk12.red("FAILED")}`);
2326
+ console.log("");
2327
+ }
2328
+ const outputPath = options.output || (fs14.existsSync(path14.join(targetDir, ".fishi")) ? path14.join(targetDir, ".fishi", "security-report.md") : null);
2329
+ if (outputPath) {
2330
+ const dir = path14.dirname(outputPath);
2331
+ if (!fs14.existsSync(dir)) fs14.mkdirSync(dir, { recursive: true });
2332
+ fs14.writeFileSync(outputPath, generateSecurityReport(report), "utf-8");
2333
+ console.log(chalk12.green(` Report saved to ${path14.relative(targetDir, outputPath)}`));
2334
+ console.log("");
2335
+ }
2336
+ if (!report.summary.passed) process.exit(1);
2337
+ } else if (action === "rules") {
2338
+ const rules = getScanRules();
2339
+ console.log("");
2340
+ console.log(chalk12.cyan.bold(" FISHI Security Scanner \u2014 Rules"));
2341
+ console.log(chalk12.gray(` ${rules.length} rules active`));
2342
+ console.log("");
2343
+ const byCategory = {};
2344
+ for (const r of rules) {
2345
+ if (!byCategory[r.category]) byCategory[r.category] = [];
2346
+ byCategory[r.category].push(r);
2347
+ }
2348
+ for (const [cat, catRules] of Object.entries(byCategory)) {
2349
+ console.log(chalk12.white.bold(` ${cat}`));
2350
+ for (const r of catRules) {
2351
+ const sevColor = r.severity === "critical" ? chalk12.red : r.severity === "high" ? chalk12.red : r.severity === "medium" ? chalk12.yellow : chalk12.blue;
2352
+ console.log(` ${sevColor(r.severity.padEnd(8))} ${r.id}${r.cwe ? chalk12.gray(` (${r.cwe})`) : ""}`);
2353
+ console.log(chalk12.gray(` ${r.message}`));
2354
+ }
2355
+ console.log("");
2356
+ }
2357
+ } else {
2358
+ console.log(chalk12.yellow(` Unknown action: ${action}. Use: scan, rules`));
2359
+ }
2360
+ }
2361
+
2259
2362
  // src/index.ts
2260
2363
  var program = new Command();
2261
2364
  program.name("fishi").description(
2262
- chalk12.cyan("\u{1F41F} FISHI") + " \u2014 Your AI Dev Team That Actually Ships\n Autonomous agent framework for Claude Code"
2263
- ).version("0.9.0");
2365
+ chalk13.cyan("\u{1F41F} FISHI") + " \u2014 Your AI Dev Team That Actually Ships\n Autonomous agent framework for Claude Code"
2366
+ ).version("0.12.0");
2264
2367
  program.command("init").description("Initialize FISHI in the current directory").argument("[description]", "Project description (skip wizard with zero-config)").option("-l, --language <lang>", "Primary language (e.g., typescript, python)").option("-f, --framework <framework>", "Framework (e.g., nextjs, express, django)").option(
2265
2368
  "-c, --cost-mode <mode>",
2266
2369
  "Cost mode: performance | balanced | economy",
@@ -2276,4 +2379,5 @@ program.command("sandbox").description("Sandbox status and policy management").a
2276
2379
  program.command("quickstart").description("Vibe mode \u2014 skip gates, scaffold + start dev server immediately").argument("[description]", "What are you building?").option("-l, --language <lang>", "Primary language").option("-f, --framework <framework>", "Framework").option("-c, --cost-mode <mode>", "Cost mode", "balanced").option("--dev-cmd <cmd>", "Custom dev server command").option("--port <port>", "Dev server port").action(quickstartCommand);
2277
2380
  program.command("preview").description("Start live preview dev server").option("--dev-cmd <cmd>", "Custom dev server command").option("--port <port>", "Dev server port").action(previewCommand);
2278
2381
  program.command("design").description("Design system \u2014 detect tokens, init design system, validate with Brand Guardian").argument("<action>", "Action: detect | init | validate").option("-o, --output <path>", "Output path for design config").action(designCommand);
2382
+ program.command("security").description("Security scanner \u2014 native SAST + OWASP vulnerability detection").argument("<action>", "Action: scan | rules").option("-o, --output <path>", "Save report to file").option("--json", "Output as JSON").action(securityCommand);
2279
2383
  program.parse();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@qlucent/fishi",
3
- "version": "0.9.0",
3
+ "version": "0.12.0",
4
4
  "description": "FISHI — Your AI Dev Team That Actually Ships. Autonomous agent framework for Claude Code.",
5
5
  "license": "MIT",
6
6
  "type": "module",