@qlucent/fishi 0.5.0 → 0.7.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.js +328 -4
- 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
|
|
5
|
+
import chalk9 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 } from "@qlucent/fishi-core";
|
|
13
|
+
import { detectConflicts, createBackup, detectDocker } from "@qlucent/fishi-core";
|
|
14
14
|
|
|
15
15
|
// src/analyzers/detector.ts
|
|
16
16
|
import fs from "fs";
|
|
@@ -1005,6 +1005,23 @@ async function initCommand(description, options) {
|
|
|
1005
1005
|
} else {
|
|
1006
1006
|
initOptions = await runWizard(options);
|
|
1007
1007
|
}
|
|
1008
|
+
const dockerAvailable = detectDocker();
|
|
1009
|
+
let sandboxMode = "process";
|
|
1010
|
+
if (options.interactive !== false) {
|
|
1011
|
+
if (dockerAvailable) {
|
|
1012
|
+
const { useSandbox } = await inquirer.prompt([{
|
|
1013
|
+
type: "confirm",
|
|
1014
|
+
name: "useSandbox",
|
|
1015
|
+
message: "Docker detected. Use Docker sandbox for agent isolation? (Recommended)",
|
|
1016
|
+
default: true
|
|
1017
|
+
}]);
|
|
1018
|
+
sandboxMode = useSandbox ? "docker" : "process";
|
|
1019
|
+
} else {
|
|
1020
|
+
console.log(chalk.yellow(" Docker not found. Using process-level sandbox (limited isolation)."));
|
|
1021
|
+
console.log(chalk.gray(" Install Docker for full agent isolation: https://docs.docker.com/get-docker/"));
|
|
1022
|
+
console.log("");
|
|
1023
|
+
}
|
|
1024
|
+
}
|
|
1008
1025
|
let brownfieldAnalysis = null;
|
|
1009
1026
|
if (detection.type === "brownfield" || detection.type === "hybrid") {
|
|
1010
1027
|
console.log("");
|
|
@@ -1148,6 +1165,21 @@ async function initCommand(description, options) {
|
|
|
1148
1165
|
const report = generateBrownfieldReport(brownfieldAnalysis);
|
|
1149
1166
|
fs3.writeFileSync(reportPath, report, "utf-8");
|
|
1150
1167
|
}
|
|
1168
|
+
const sandboxYaml = `
|
|
1169
|
+
sandbox:
|
|
1170
|
+
mode: ${sandboxMode}
|
|
1171
|
+
docker_available: ${dockerAvailable}
|
|
1172
|
+
`;
|
|
1173
|
+
const fishiYamlPath = path3.join(targetDir, ".fishi", "fishi.yaml");
|
|
1174
|
+
if (fs3.existsSync(fishiYamlPath)) {
|
|
1175
|
+
fs3.appendFileSync(fishiYamlPath, sandboxYaml, "utf-8");
|
|
1176
|
+
}
|
|
1177
|
+
const { getSandboxPolicyTemplate, getDockerfileTemplate } = await import("@qlucent/fishi-core");
|
|
1178
|
+
fs3.writeFileSync(path3.join(targetDir, ".fishi", "sandbox-policy.yaml"), getSandboxPolicyTemplate(), "utf-8");
|
|
1179
|
+
if (sandboxMode === "docker") {
|
|
1180
|
+
fs3.mkdirSync(path3.join(targetDir, ".fishi", "docker"), { recursive: true });
|
|
1181
|
+
fs3.writeFileSync(path3.join(targetDir, ".fishi", "docker", "Dockerfile"), getDockerfileTemplate(), "utf-8");
|
|
1182
|
+
}
|
|
1151
1183
|
scaffoldSpinner.succeed("FISHI framework scaffolded successfully!");
|
|
1152
1184
|
console.log("");
|
|
1153
1185
|
console.log(chalk.bold(" Created:"));
|
|
@@ -1158,6 +1190,7 @@ async function initCommand(description, options) {
|
|
|
1158
1190
|
console.log(chalk.gray(` \u{1F4C4} .claude/CLAUDE.md \u2014 Project instructions`));
|
|
1159
1191
|
console.log(chalk.gray(` \u{1F4C4} .claude/settings.json \u2014 Hooks & permissions`));
|
|
1160
1192
|
console.log(chalk.gray(` \u{1F4C4} .mcp.json \u2014 MCP server config`));
|
|
1193
|
+
console.log(chalk.gray(` \u{1F512} Sandbox: ${sandboxMode} mode${sandboxMode === "docker" ? " (full isolation)" : " (limited isolation)"}`));
|
|
1161
1194
|
if (brownfieldAnalysis) {
|
|
1162
1195
|
console.log(chalk.gray(` \u{1F4C4} .fishi/memory/brownfield-analysis.md \u2014 Codebase analysis report`));
|
|
1163
1196
|
console.log("");
|
|
@@ -1649,11 +1682,299 @@ async function validateCommand() {
|
|
|
1649
1682
|
}
|
|
1650
1683
|
}
|
|
1651
1684
|
|
|
1685
|
+
// src/commands/monitor.ts
|
|
1686
|
+
import chalk6 from "chalk";
|
|
1687
|
+
import fs8 from "fs";
|
|
1688
|
+
import path8 from "path";
|
|
1689
|
+
import { parse as parseYaml4 } from "yaml";
|
|
1690
|
+
import { readMonitorState, getAgentSummary } from "@qlucent/fishi-core";
|
|
1691
|
+
var PHASES = ["init", "discovery", "planning", "development", "testing", "review", "deployed"];
|
|
1692
|
+
function formatNumber(n) {
|
|
1693
|
+
if (n >= 1e6) return `${(n / 1e6).toFixed(1)}M`;
|
|
1694
|
+
if (n >= 1e3) return `${(n / 1e3).toFixed(1)}K`;
|
|
1695
|
+
return String(n);
|
|
1696
|
+
}
|
|
1697
|
+
function renderPhaseBar(currentPhase) {
|
|
1698
|
+
const idx = PHASES.indexOf(currentPhase);
|
|
1699
|
+
const parts = PHASES.map((p, i) => {
|
|
1700
|
+
if (i < idx) return chalk6.green(`[${p}]`);
|
|
1701
|
+
if (i === idx) return chalk6.cyan.bold(`[${p}]`);
|
|
1702
|
+
return chalk6.gray(`[${p}]`);
|
|
1703
|
+
});
|
|
1704
|
+
return " " + parts.join(chalk6.gray(" \u2192 "));
|
|
1705
|
+
}
|
|
1706
|
+
function renderDivider(label) {
|
|
1707
|
+
const line = "\u2500".repeat(60);
|
|
1708
|
+
return chalk6.gray(`
|
|
1709
|
+
${line}
|
|
1710
|
+
`) + chalk6.bold(label) + "\n";
|
|
1711
|
+
}
|
|
1712
|
+
function renderMonitor(projectDir) {
|
|
1713
|
+
const state = readMonitorState(projectDir);
|
|
1714
|
+
const agentSummary = getAgentSummary(projectDir);
|
|
1715
|
+
let currentPhase = "init";
|
|
1716
|
+
const projectYamlPath = path8.join(projectDir, ".fishi", "state", "project.yaml");
|
|
1717
|
+
if (fs8.existsSync(projectYamlPath)) {
|
|
1718
|
+
try {
|
|
1719
|
+
const projectState = parseYaml4(fs8.readFileSync(projectYamlPath, "utf-8"));
|
|
1720
|
+
currentPhase = projectState?.current_phase || "init";
|
|
1721
|
+
} catch {
|
|
1722
|
+
}
|
|
1723
|
+
}
|
|
1724
|
+
let gates = [];
|
|
1725
|
+
const gatesYamlPath = path8.join(projectDir, ".fishi", "state", "gates.yaml");
|
|
1726
|
+
if (fs8.existsSync(gatesYamlPath)) {
|
|
1727
|
+
try {
|
|
1728
|
+
const gatesData = parseYaml4(fs8.readFileSync(gatesYamlPath, "utf-8"));
|
|
1729
|
+
gates = gatesData?.gates || [];
|
|
1730
|
+
} catch {
|
|
1731
|
+
}
|
|
1732
|
+
}
|
|
1733
|
+
const { summary, dynamicAgents, events, lastUpdated } = state;
|
|
1734
|
+
console.log("");
|
|
1735
|
+
console.log(chalk6.cyan.bold(" FISHI Agent Monitor"));
|
|
1736
|
+
console.log(chalk6.gray(` Last updated: ${new Date(lastUpdated).toLocaleTimeString()}`));
|
|
1737
|
+
console.log(renderDivider("Phase Progress"));
|
|
1738
|
+
console.log(renderPhaseBar(currentPhase));
|
|
1739
|
+
console.log("");
|
|
1740
|
+
console.log(renderDivider("Summary"));
|
|
1741
|
+
console.log(
|
|
1742
|
+
` ${chalk6.bold("Completions:")} ${chalk6.cyan(summary.totalAgentCompletions)} ${chalk6.bold("Files Changed:")} ${chalk6.cyan(summary.totalFilesChanged)} ${chalk6.bold("Tokens:")} ${chalk6.cyan(formatNumber(summary.totalTokens))} ${chalk6.bold("Dynamic Agents:")} ${chalk6.cyan(summary.dynamicAgentsCreated)}`
|
|
1743
|
+
);
|
|
1744
|
+
console.log(renderDivider("Tokens by Model"));
|
|
1745
|
+
const modelEntries = Object.entries(summary.tokensByModel);
|
|
1746
|
+
if (modelEntries.length === 0) {
|
|
1747
|
+
console.log(chalk6.gray(" (none yet)"));
|
|
1748
|
+
} else {
|
|
1749
|
+
for (const [model, tokens] of modelEntries.sort((a, b) => b[1] - a[1])) {
|
|
1750
|
+
console.log(` ${chalk6.yellow(model.padEnd(30))} ${chalk6.cyan(formatNumber(tokens))}`);
|
|
1751
|
+
}
|
|
1752
|
+
}
|
|
1753
|
+
console.log(renderDivider("Tools Used"));
|
|
1754
|
+
const toolEntries = Object.entries(summary.toolsUsed);
|
|
1755
|
+
if (toolEntries.length === 0) {
|
|
1756
|
+
console.log(chalk6.gray(" (none yet)"));
|
|
1757
|
+
} else {
|
|
1758
|
+
for (const [tool, count] of toolEntries.sort((a, b) => b[1] - a[1]).slice(0, 10)) {
|
|
1759
|
+
console.log(` ${chalk6.yellow(tool.padEnd(30))} ${chalk6.cyan(count)}`);
|
|
1760
|
+
}
|
|
1761
|
+
}
|
|
1762
|
+
console.log(renderDivider("Agent Activity"));
|
|
1763
|
+
const agentEntries = Object.entries(agentSummary);
|
|
1764
|
+
if (agentEntries.length === 0) {
|
|
1765
|
+
console.log(chalk6.gray(" (no agent events yet)"));
|
|
1766
|
+
} else {
|
|
1767
|
+
for (const [agent, stats] of agentEntries.sort((a, b) => b[1].completions - a[1].completions)) {
|
|
1768
|
+
const failColor = stats.failures > 0 ? chalk6.red : chalk6.gray;
|
|
1769
|
+
console.log(
|
|
1770
|
+
` ${chalk6.magenta(agent.padEnd(28))} completions: ${chalk6.cyan(stats.completions)} failures: ${failColor(stats.failures)} files: ${chalk6.cyan(stats.filesChanged)}`
|
|
1771
|
+
);
|
|
1772
|
+
}
|
|
1773
|
+
}
|
|
1774
|
+
if (dynamicAgents.length > 0) {
|
|
1775
|
+
console.log(renderDivider("Dynamic Agents"));
|
|
1776
|
+
for (const da of dynamicAgents) {
|
|
1777
|
+
console.log(` ${chalk6.magenta(da.name)} ${chalk6.gray("\u2190")} ${chalk6.gray(da.coordinator)}`);
|
|
1778
|
+
}
|
|
1779
|
+
}
|
|
1780
|
+
if (gates.length > 0) {
|
|
1781
|
+
console.log(renderDivider("Gates"));
|
|
1782
|
+
for (const gate of gates) {
|
|
1783
|
+
const statusColor = gate.status === "passed" ? chalk6.green : gate.status === "failed" ? chalk6.red : chalk6.yellow;
|
|
1784
|
+
console.log(` ${statusColor("\u25CF")} ${gate.name.padEnd(30)} ${statusColor(gate.status)}`);
|
|
1785
|
+
}
|
|
1786
|
+
}
|
|
1787
|
+
console.log(renderDivider("Recent Events (last 10)"));
|
|
1788
|
+
const recentEvents = events.slice(-10).reverse();
|
|
1789
|
+
if (recentEvents.length === 0) {
|
|
1790
|
+
console.log(chalk6.gray(" (no events yet)"));
|
|
1791
|
+
} else {
|
|
1792
|
+
for (const ev of recentEvents) {
|
|
1793
|
+
const ts = new Date(ev.timestamp).toLocaleTimeString();
|
|
1794
|
+
console.log(
|
|
1795
|
+
` ${chalk6.gray(ts)} ${chalk6.cyan(ev.type.padEnd(20))} ${chalk6.magenta(ev.agent)}`
|
|
1796
|
+
);
|
|
1797
|
+
}
|
|
1798
|
+
}
|
|
1799
|
+
console.log("");
|
|
1800
|
+
}
|
|
1801
|
+
async function monitorCommand(options) {
|
|
1802
|
+
const projectDir = process.cwd();
|
|
1803
|
+
const fishiDir = path8.join(projectDir, ".fishi");
|
|
1804
|
+
if (!fs8.existsSync(fishiDir)) {
|
|
1805
|
+
console.log(chalk6.yellow("\n FISHI is not initialized in this directory."));
|
|
1806
|
+
console.log(chalk6.gray(" Run `fishi init` to get started.\n"));
|
|
1807
|
+
return;
|
|
1808
|
+
}
|
|
1809
|
+
if (!options.watch) {
|
|
1810
|
+
renderMonitor(projectDir);
|
|
1811
|
+
return;
|
|
1812
|
+
}
|
|
1813
|
+
const monitorJsonPath = path8.join(fishiDir, "state", "monitor.json");
|
|
1814
|
+
let lastMtime = 0;
|
|
1815
|
+
const render = () => {
|
|
1816
|
+
process.stdout.write("\x1B[2J\x1B[0;0H");
|
|
1817
|
+
renderMonitor(projectDir);
|
|
1818
|
+
console.log(chalk6.gray(" [watch mode \u2014 polling every 1s, Ctrl+C to exit]"));
|
|
1819
|
+
};
|
|
1820
|
+
render();
|
|
1821
|
+
const interval = setInterval(() => {
|
|
1822
|
+
try {
|
|
1823
|
+
if (fs8.existsSync(monitorJsonPath)) {
|
|
1824
|
+
const mtime = fs8.statSync(monitorJsonPath).mtimeMs;
|
|
1825
|
+
if (mtime !== lastMtime) {
|
|
1826
|
+
lastMtime = mtime;
|
|
1827
|
+
render();
|
|
1828
|
+
}
|
|
1829
|
+
}
|
|
1830
|
+
} catch {
|
|
1831
|
+
}
|
|
1832
|
+
}, 1e3);
|
|
1833
|
+
process.on("SIGINT", () => {
|
|
1834
|
+
clearInterval(interval);
|
|
1835
|
+
console.log(chalk6.gray("\n Monitor exited.\n"));
|
|
1836
|
+
process.exit(0);
|
|
1837
|
+
});
|
|
1838
|
+
}
|
|
1839
|
+
|
|
1840
|
+
// src/commands/dashboard.ts
|
|
1841
|
+
import http from "http";
|
|
1842
|
+
import fs9 from "fs";
|
|
1843
|
+
import path9 from "path";
|
|
1844
|
+
import { parse as parseYaml5 } from "yaml";
|
|
1845
|
+
import chalk7 from "chalk";
|
|
1846
|
+
import { readMonitorState as readMonitorState2, getAgentSummary as getAgentSummary2, getDashboardHtml } from "@qlucent/fishi-core";
|
|
1847
|
+
var DEFAULT_PORT = 4269;
|
|
1848
|
+
async function dashboardCommand(options) {
|
|
1849
|
+
const projectDir = process.cwd();
|
|
1850
|
+
const fishiDir = path9.join(projectDir, ".fishi");
|
|
1851
|
+
if (!fs9.existsSync(fishiDir)) {
|
|
1852
|
+
console.log(chalk7.yellow("\n FISHI is not initialized in this directory."));
|
|
1853
|
+
console.log(chalk7.gray(" Run `fishi init` to get started.\n"));
|
|
1854
|
+
return;
|
|
1855
|
+
}
|
|
1856
|
+
const port = Number(options.port || DEFAULT_PORT);
|
|
1857
|
+
if (isNaN(port) || port < 1 || port > 65535) {
|
|
1858
|
+
console.error(chalk7.red(` Invalid port: ${options.port}`));
|
|
1859
|
+
process.exit(1);
|
|
1860
|
+
}
|
|
1861
|
+
const html = getDashboardHtml();
|
|
1862
|
+
const server = http.createServer((req, res) => {
|
|
1863
|
+
const url = req.url?.split("?")[0] ?? "/";
|
|
1864
|
+
if (url === "/") {
|
|
1865
|
+
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
|
|
1866
|
+
res.end(html);
|
|
1867
|
+
return;
|
|
1868
|
+
}
|
|
1869
|
+
if (url === "/api/state") {
|
|
1870
|
+
try {
|
|
1871
|
+
const monitorState = readMonitorState2(projectDir);
|
|
1872
|
+
const agentSummary = getAgentSummary2(projectDir);
|
|
1873
|
+
let phase = "init";
|
|
1874
|
+
const projectYamlPath = path9.join(fishiDir, "state", "project.yaml");
|
|
1875
|
+
if (fs9.existsSync(projectYamlPath)) {
|
|
1876
|
+
try {
|
|
1877
|
+
const projectState = parseYaml5(fs9.readFileSync(projectYamlPath, "utf-8"));
|
|
1878
|
+
phase = projectState?.current_phase || "init";
|
|
1879
|
+
} catch {
|
|
1880
|
+
}
|
|
1881
|
+
}
|
|
1882
|
+
let gates = [];
|
|
1883
|
+
const gatesYamlPath = path9.join(fishiDir, "state", "gates.yaml");
|
|
1884
|
+
if (fs9.existsSync(gatesYamlPath)) {
|
|
1885
|
+
try {
|
|
1886
|
+
const gatesData = parseYaml5(fs9.readFileSync(gatesYamlPath, "utf-8"));
|
|
1887
|
+
gates = gatesData?.gates || [];
|
|
1888
|
+
} catch {
|
|
1889
|
+
}
|
|
1890
|
+
}
|
|
1891
|
+
const payload = {
|
|
1892
|
+
summary: monitorState.summary,
|
|
1893
|
+
events: monitorState.events,
|
|
1894
|
+
dynamicAgents: monitorState.dynamicAgents,
|
|
1895
|
+
lastUpdated: monitorState.lastUpdated,
|
|
1896
|
+
agentSummary,
|
|
1897
|
+
phase,
|
|
1898
|
+
gates
|
|
1899
|
+
};
|
|
1900
|
+
res.writeHead(200, {
|
|
1901
|
+
"Content-Type": "application/json",
|
|
1902
|
+
"Access-Control-Allow-Origin": "*"
|
|
1903
|
+
});
|
|
1904
|
+
res.end(JSON.stringify(payload));
|
|
1905
|
+
} catch (err) {
|
|
1906
|
+
res.writeHead(500, { "Content-Type": "application/json" });
|
|
1907
|
+
res.end(JSON.stringify({ error: String(err) }));
|
|
1908
|
+
}
|
|
1909
|
+
return;
|
|
1910
|
+
}
|
|
1911
|
+
res.writeHead(404, { "Content-Type": "text/plain" });
|
|
1912
|
+
res.end("Not found");
|
|
1913
|
+
});
|
|
1914
|
+
server.listen(port, () => {
|
|
1915
|
+
console.log("");
|
|
1916
|
+
console.log(chalk7.cyan.bold(" FISHI Agent Dashboard"));
|
|
1917
|
+
console.log("");
|
|
1918
|
+
console.log(` ${chalk7.bold("URL:")} ${chalk7.cyan(`http://localhost:${port}`)}`);
|
|
1919
|
+
console.log(` ${chalk7.bold("API:")} ${chalk7.gray(`http://localhost:${port}/api/state`)}`);
|
|
1920
|
+
console.log("");
|
|
1921
|
+
console.log(chalk7.gray(" Press Ctrl+C to stop.\n"));
|
|
1922
|
+
});
|
|
1923
|
+
process.on("SIGINT", () => {
|
|
1924
|
+
server.close(() => {
|
|
1925
|
+
console.log(chalk7.gray("\n Dashboard stopped.\n"));
|
|
1926
|
+
process.exit(0);
|
|
1927
|
+
});
|
|
1928
|
+
});
|
|
1929
|
+
await new Promise((resolve) => {
|
|
1930
|
+
server.on("close", resolve);
|
|
1931
|
+
});
|
|
1932
|
+
}
|
|
1933
|
+
|
|
1934
|
+
// src/commands/sandbox.ts
|
|
1935
|
+
import chalk8 from "chalk";
|
|
1936
|
+
import fs10 from "fs";
|
|
1937
|
+
import path10 from "path";
|
|
1938
|
+
import { detectDocker as detectDocker2, readSandboxConfig, readSandboxPolicy } from "@qlucent/fishi-core";
|
|
1939
|
+
async function sandboxCommand(action) {
|
|
1940
|
+
const targetDir = process.cwd();
|
|
1941
|
+
if (!fs10.existsSync(path10.join(targetDir, ".fishi"))) {
|
|
1942
|
+
console.log(chalk8.yellow(" No FISHI project found. Run `fishi init` first."));
|
|
1943
|
+
return;
|
|
1944
|
+
}
|
|
1945
|
+
if (action === "status") {
|
|
1946
|
+
const config = readSandboxConfig(targetDir);
|
|
1947
|
+
const dockerNow = detectDocker2();
|
|
1948
|
+
console.log("");
|
|
1949
|
+
console.log(chalk8.cyan.bold(" FISHI Sandbox Status"));
|
|
1950
|
+
console.log("");
|
|
1951
|
+
console.log(chalk8.white(" Mode: ") + (config.mode === "docker" ? chalk8.green("Docker") : chalk8.yellow("Process")));
|
|
1952
|
+
console.log(chalk8.white(" Docker available: ") + (dockerNow ? chalk8.green("yes") : chalk8.red("no")));
|
|
1953
|
+
if (config.mode === "docker" && !dockerNow) {
|
|
1954
|
+
console.log(chalk8.red(" Warning: Docker mode configured but Docker not available!"));
|
|
1955
|
+
}
|
|
1956
|
+
console.log("");
|
|
1957
|
+
const policy = readSandboxPolicy(targetDir);
|
|
1958
|
+
console.log(chalk8.white.bold(" Policy"));
|
|
1959
|
+
console.log(chalk8.gray(` Timeout: ${policy.timeout}s`));
|
|
1960
|
+
console.log(chalk8.gray(` Memory limit: ${policy.memory}`));
|
|
1961
|
+
console.log(chalk8.gray(` CPU limit: ${policy.cpus}`));
|
|
1962
|
+
console.log(chalk8.gray(` Network allow: ${policy.networkAllow.join(", ")}`));
|
|
1963
|
+
console.log(chalk8.gray(` Env passthrough: ${policy.envPassthrough.length > 0 ? policy.envPassthrough.join(", ") : "(none)"}`));
|
|
1964
|
+
console.log("");
|
|
1965
|
+
} else if (action === "policy") {
|
|
1966
|
+
const policy = readSandboxPolicy(targetDir);
|
|
1967
|
+
console.log(JSON.stringify(policy, null, 2));
|
|
1968
|
+
} else {
|
|
1969
|
+
console.log(chalk8.yellow(` Unknown action: ${action}. Use: status, policy`));
|
|
1970
|
+
}
|
|
1971
|
+
}
|
|
1972
|
+
|
|
1652
1973
|
// src/index.ts
|
|
1653
1974
|
var program = new Command();
|
|
1654
1975
|
program.name("fishi").description(
|
|
1655
|
-
|
|
1656
|
-
).version("0.
|
|
1976
|
+
chalk9.cyan("\u{1F41F} FISHI") + " \u2014 Your AI Dev Team That Actually Ships\n Autonomous agent framework for Claude Code"
|
|
1977
|
+
).version("0.7.0");
|
|
1657
1978
|
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(
|
|
1658
1979
|
"-c, --cost-mode <mode>",
|
|
1659
1980
|
"Cost mode: performance | balanced | economy",
|
|
@@ -1663,4 +1984,7 @@ program.command("status").description("Show project status, active agents, and T
|
|
|
1663
1984
|
program.command("mcp").description("Manage MCP server integrations").argument("<action>", "Action: add | list | remove").argument("[name]", "MCP server name").action(mcpCommand);
|
|
1664
1985
|
program.command("reset").description("Rollback to a previous checkpoint").argument("[checkpoint]", "Checkpoint ID (defaults to latest)").action(resetCommand);
|
|
1665
1986
|
program.command("validate").description("Validate scaffold integrity \u2014 checks files, frontmatter, cross-references, pipeline, and permissions").action(validateCommand);
|
|
1987
|
+
program.command("monitor").description("Agent observability \u2014 TUI dashboard showing agent activity, tokens, gates").option("-w, --watch", "Watch mode \u2014 auto-refresh on changes").action(monitorCommand);
|
|
1988
|
+
program.command("dashboard").description("Agent observability \u2014 web dashboard at http://localhost:4269").option("-p, --port <port>", "Port number", "4269").action(dashboardCommand);
|
|
1989
|
+
program.command("sandbox").description("Sandbox status and policy management").argument("<action>", "Action: status | policy").action(sandboxCommand);
|
|
1666
1990
|
program.parse();
|