@forcefield/mcp-server 0.1.7 → 0.1.8
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/README.md +13 -5
- package/build/{chunk-4FKA7CA3.js → chunk-HSGOG52U.js} +5 -5
- package/build/chunk-HSGOG52U.js.map +1 -0
- package/build/{chunk-7XLGRGP6.js → chunk-K464TYAJ.js} +3 -3
- package/build/{chunk-7XLGRGP6.js.map → chunk-K464TYAJ.js.map} +1 -1
- package/build/{chunk-6YANB5QT.js → chunk-UOMOGP46.js} +3 -1
- package/build/{chunk-6YANB5QT.js.map → chunk-UOMOGP46.js.map} +1 -1
- package/build/cli/index.js +292 -6
- package/build/cli/index.js.map +1 -1
- package/build/index.js +2 -2
- package/build/setup/index.js +2 -2
- package/package.json +1 -1
- package/workflows/ff-help.md +1 -1
- package/workflows/ff-start.md +1 -1
- package/build/chunk-4FKA7CA3.js.map +0 -1
|
@@ -3,13 +3,15 @@
|
|
|
3
3
|
// src/supabase-defaults.ts
|
|
4
4
|
var PRODUCTION_SUPABASE_URL = "https://ffhwxtbenauaimiwvqed.supabase.co";
|
|
5
5
|
var PRODUCTION_SUPABASE_PUBLISHABLE_KEY = "sb_publishable_AvzTXmuoTKrIb2mPk4dzdQ_gR7GrpMW";
|
|
6
|
+
var LOCAL_SUPABASE_URL = "http://127.0.0.1:54321";
|
|
6
7
|
var LOCAL_SUPABASE_ANON_KEY = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6ImFub24iLCJleHAiOjE5ODM4MTI5OTZ9.CRXP1A7WOeoJeXxjNni43kdQwgnWNReilDMblYTn_I0";
|
|
7
8
|
var LOCAL_SUPABASE_SERVICE_ROLE_KEY = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6InNlcnZpY2Vfcm9sZSIsImV4cCI6MTk4MzgxMjk5Nn0.EGIM96RAZx35lJzdJsyH-qQwv8Hdp7fsn3W0YpN81IU";
|
|
8
9
|
|
|
9
10
|
export {
|
|
10
11
|
PRODUCTION_SUPABASE_URL,
|
|
11
12
|
PRODUCTION_SUPABASE_PUBLISHABLE_KEY,
|
|
13
|
+
LOCAL_SUPABASE_URL,
|
|
12
14
|
LOCAL_SUPABASE_ANON_KEY,
|
|
13
15
|
LOCAL_SUPABASE_SERVICE_ROLE_KEY
|
|
14
16
|
};
|
|
15
|
-
//# sourceMappingURL=chunk-
|
|
17
|
+
//# sourceMappingURL=chunk-UOMOGP46.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/supabase-defaults.ts"],"sourcesContent":["/**\n * Canonical Supabase defaults for hosted production and local development.\n * Production defaults are used unless local/dev mode is explicitly enabled.\n */\n\nexport const PRODUCTION_SUPABASE_URL = 'https://ffhwxtbenauaimiwvqed.supabase.co';\nexport const PRODUCTION_SUPABASE_PUBLISHABLE_KEY =\n 'sb_publishable_AvzTXmuoTKrIb2mPk4dzdQ_gR7GrpMW';\n\nexport const LOCAL_SUPABASE_URL = 'http://127.0.0.1:54321';\nexport const LOCAL_SUPABASE_ANON_KEY =\n 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6ImFub24iLCJleHAiOjE5ODM4MTI5OTZ9.CRXP1A7WOeoJeXxjNni43kdQwgnWNReilDMblYTn_I0';\nexport const LOCAL_SUPABASE_SERVICE_ROLE_KEY =\n 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6InNlcnZpY2Vfcm9sZSIsImV4cCI6MTk4MzgxMjk5Nn0.EGIM96RAZx35lJzdJsyH-qQwv8Hdp7fsn3W0YpN81IU';\n"],"mappings":";;;AAKO,IAAM,0BAA0B;AAChC,IAAM,sCACX;
|
|
1
|
+
{"version":3,"sources":["../src/supabase-defaults.ts"],"sourcesContent":["/**\n * Canonical Supabase defaults for hosted production and local development.\n * Production defaults are used unless local/dev mode is explicitly enabled.\n */\n\nexport const PRODUCTION_SUPABASE_URL = 'https://ffhwxtbenauaimiwvqed.supabase.co';\nexport const PRODUCTION_SUPABASE_PUBLISHABLE_KEY =\n 'sb_publishable_AvzTXmuoTKrIb2mPk4dzdQ_gR7GrpMW';\n\nexport const LOCAL_SUPABASE_URL = 'http://127.0.0.1:54321';\nexport const LOCAL_SUPABASE_ANON_KEY =\n 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6ImFub24iLCJleHAiOjE5ODM4MTI5OTZ9.CRXP1A7WOeoJeXxjNni43kdQwgnWNReilDMblYTn_I0';\nexport const LOCAL_SUPABASE_SERVICE_ROLE_KEY =\n 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6InNlcnZpY2Vfcm9sZSIsImV4cCI6MTk4MzgxMjk5Nn0.EGIM96RAZx35lJzdJsyH-qQwv8Hdp7fsn3W0YpN81IU';\n"],"mappings":";;;AAKO,IAAM,0BAA0B;AAChC,IAAM,sCACX;AAEK,IAAM,qBAAqB;AAC3B,IAAM,0BACX;AACK,IAAM,kCACX;","names":[]}
|
package/build/cli/index.js
CHANGED
|
@@ -1,25 +1,305 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
runMcpCli
|
|
4
|
-
} from "../chunk-
|
|
4
|
+
} from "../chunk-K464TYAJ.js";
|
|
5
5
|
import {
|
|
6
6
|
runSetupCli
|
|
7
|
-
} from "../chunk-
|
|
8
|
-
import
|
|
7
|
+
} from "../chunk-HSGOG52U.js";
|
|
8
|
+
import {
|
|
9
|
+
LOCAL_SUPABASE_URL,
|
|
10
|
+
PRODUCTION_SUPABASE_PUBLISHABLE_KEY,
|
|
11
|
+
PRODUCTION_SUPABASE_URL
|
|
12
|
+
} from "../chunk-UOMOGP46.js";
|
|
9
13
|
|
|
10
14
|
// cli/index.ts
|
|
11
15
|
import { pathToFileURL } from "url";
|
|
16
|
+
|
|
17
|
+
// cli/dashboard.ts
|
|
18
|
+
import { spawn } from "child_process";
|
|
19
|
+
import { existsSync } from "fs";
|
|
20
|
+
import { writeFile } from "fs/promises";
|
|
21
|
+
import { join, resolve } from "path";
|
|
22
|
+
import pc from "picocolors";
|
|
23
|
+
var DEFAULT_DIR = "./forcefield-dashboard";
|
|
24
|
+
function npmCmd() {
|
|
25
|
+
return process.platform === "win32" ? "npm.cmd" : "npm";
|
|
26
|
+
}
|
|
27
|
+
function printUsage() {
|
|
28
|
+
console.log(
|
|
29
|
+
[
|
|
30
|
+
"Forcefield Dashboard",
|
|
31
|
+
"",
|
|
32
|
+
"Usage:",
|
|
33
|
+
" forcefield dashboard start [--dir <path>] [--local] [--force] [--no-install] [--no-run]",
|
|
34
|
+
" forcefield dashboard --help",
|
|
35
|
+
"",
|
|
36
|
+
"Examples:",
|
|
37
|
+
" forcefield dashboard start",
|
|
38
|
+
" forcefield dashboard start --dir ./dashboard",
|
|
39
|
+
" forcefield dashboard start --local --force"
|
|
40
|
+
].join("\n")
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
function parseDashboardCommand(args) {
|
|
44
|
+
const defaultOptions = {
|
|
45
|
+
dir: DEFAULT_DIR,
|
|
46
|
+
local: false,
|
|
47
|
+
force: false,
|
|
48
|
+
install: true,
|
|
49
|
+
run: true
|
|
50
|
+
};
|
|
51
|
+
if (args.includes("--help") || args.includes("-h")) {
|
|
52
|
+
return { type: "help", options: defaultOptions };
|
|
53
|
+
}
|
|
54
|
+
const first = args[0];
|
|
55
|
+
const isStart = !first || first === "start";
|
|
56
|
+
if (!isStart) {
|
|
57
|
+
return {
|
|
58
|
+
type: "help",
|
|
59
|
+
invalidCommand: first,
|
|
60
|
+
options: defaultOptions
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
const optionArgs = first === "start" ? args.slice(1) : args;
|
|
64
|
+
const options = { ...defaultOptions };
|
|
65
|
+
for (let i = 0; i < optionArgs.length; i += 1) {
|
|
66
|
+
const arg = optionArgs[i];
|
|
67
|
+
if (arg === "--dir" || arg === "-d") {
|
|
68
|
+
const value = optionArgs[i + 1];
|
|
69
|
+
if (!value || value.startsWith("-")) {
|
|
70
|
+
return {
|
|
71
|
+
type: "help",
|
|
72
|
+
parseError: "Missing value for --dir.",
|
|
73
|
+
options: defaultOptions
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
options.dir = value;
|
|
77
|
+
i += 1;
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
if (arg === "--local") {
|
|
81
|
+
options.local = true;
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
if (arg === "--force" || arg === "-f") {
|
|
85
|
+
options.force = true;
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
if (arg === "--no-install") {
|
|
89
|
+
options.install = false;
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
if (arg === "--no-run") {
|
|
93
|
+
options.run = false;
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
return {
|
|
97
|
+
type: "help",
|
|
98
|
+
parseError: `Unknown option: ${arg}`,
|
|
99
|
+
options: defaultOptions
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
return {
|
|
103
|
+
type: "start",
|
|
104
|
+
options
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
async function runProcess(command, args, options) {
|
|
108
|
+
return new Promise((resolvePromise, rejectPromise) => {
|
|
109
|
+
const child = spawn(command, args, {
|
|
110
|
+
cwd: options?.cwd,
|
|
111
|
+
env: process.env,
|
|
112
|
+
stdio: options?.capture ? ["ignore", "pipe", "pipe"] : "inherit"
|
|
113
|
+
});
|
|
114
|
+
let stdout = "";
|
|
115
|
+
let stderr = "";
|
|
116
|
+
if (options?.capture) {
|
|
117
|
+
child.stdout.on("data", (chunk) => {
|
|
118
|
+
stdout += String(chunk);
|
|
119
|
+
});
|
|
120
|
+
child.stderr.on("data", (chunk) => {
|
|
121
|
+
stderr += String(chunk);
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
child.on("error", (error) => {
|
|
125
|
+
rejectPromise(error);
|
|
126
|
+
});
|
|
127
|
+
child.on("close", (code) => {
|
|
128
|
+
resolvePromise({ code: code ?? 1, stdout, stderr });
|
|
129
|
+
});
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
function parseEnvAssignments(raw) {
|
|
133
|
+
const env = {};
|
|
134
|
+
for (const line of raw.split(/\r?\n/)) {
|
|
135
|
+
const trimmed = line.trim();
|
|
136
|
+
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
137
|
+
const match = trimmed.match(/^(?:export\s+)?([A-Za-z_][A-Za-z0-9_]*)=(.*)$/);
|
|
138
|
+
if (!match) continue;
|
|
139
|
+
const key = match[1];
|
|
140
|
+
let value = match[2] ?? "";
|
|
141
|
+
if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
|
|
142
|
+
value = value.slice(1, -1);
|
|
143
|
+
}
|
|
144
|
+
env[key] = value;
|
|
145
|
+
}
|
|
146
|
+
return env;
|
|
147
|
+
}
|
|
148
|
+
function formatManualFallback(targetDir, force) {
|
|
149
|
+
return [
|
|
150
|
+
"Manual fallback:",
|
|
151
|
+
` npx -y @forcefield/dashboard-template@beta init --dir ${targetDir}${force ? " --force" : ""}`
|
|
152
|
+
].join("\n");
|
|
153
|
+
}
|
|
154
|
+
var defaultOps = {
|
|
155
|
+
async scaffoldDashboard(targetDir, force) {
|
|
156
|
+
const args = [
|
|
157
|
+
"exec",
|
|
158
|
+
"--yes",
|
|
159
|
+
"--package=@forcefield/dashboard-template@beta",
|
|
160
|
+
"forcefield-dashboard",
|
|
161
|
+
"--",
|
|
162
|
+
"init",
|
|
163
|
+
"--dir",
|
|
164
|
+
targetDir
|
|
165
|
+
];
|
|
166
|
+
if (force) {
|
|
167
|
+
args.push("--force");
|
|
168
|
+
}
|
|
169
|
+
try {
|
|
170
|
+
const result = await runProcess(npmCmd(), args);
|
|
171
|
+
if (result.code !== 0) {
|
|
172
|
+
throw new Error(`Scaffold command exited with code ${result.code}.`);
|
|
173
|
+
}
|
|
174
|
+
} catch (error) {
|
|
175
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
176
|
+
throw new Error(
|
|
177
|
+
`Failed to scaffold dashboard automatically: ${message}
|
|
178
|
+
${formatManualFallback(targetDir, force)}`
|
|
179
|
+
);
|
|
180
|
+
}
|
|
181
|
+
},
|
|
182
|
+
async getLocalEnv() {
|
|
183
|
+
let result;
|
|
184
|
+
try {
|
|
185
|
+
result = await runProcess("supabase", ["status", "-o", "env"], { capture: true });
|
|
186
|
+
} catch (error) {
|
|
187
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
188
|
+
throw new Error(
|
|
189
|
+
`Could not run 'supabase status -o env': ${message}. Install Supabase CLI and run ${pc.cyan("supabase start")}, or remove ${pc.cyan("--local")}.`
|
|
190
|
+
);
|
|
191
|
+
}
|
|
192
|
+
if (result.code !== 0) {
|
|
193
|
+
const detail = (result.stderr || result.stdout).trim() || `exit code ${result.code}`;
|
|
194
|
+
throw new Error(
|
|
195
|
+
`Local Supabase is unavailable (${detail}). Run ${pc.cyan("supabase start")} first, or remove ${pc.cyan("--local")}.`
|
|
196
|
+
);
|
|
197
|
+
}
|
|
198
|
+
const env = parseEnvAssignments(result.stdout);
|
|
199
|
+
const supabaseUrl = env.SUPABASE_URL || LOCAL_SUPABASE_URL;
|
|
200
|
+
const anonKey = env.SUPABASE_ANON_KEY || env.ANON_KEY;
|
|
201
|
+
if (!anonKey) {
|
|
202
|
+
throw new Error(
|
|
203
|
+
`Could not find an anon key from ${pc.cyan("supabase status -o env")}. Ensure local stack is running, or remove ${pc.cyan("--local")}.`
|
|
204
|
+
);
|
|
205
|
+
}
|
|
206
|
+
return { supabaseUrl, anonKey };
|
|
207
|
+
},
|
|
208
|
+
async installDependencies(targetDir) {
|
|
209
|
+
const result = await runProcess(npmCmd(), ["install"], { cwd: targetDir });
|
|
210
|
+
if (result.code !== 0) {
|
|
211
|
+
throw new Error(`Dependency install failed (exit ${result.code}).`);
|
|
212
|
+
}
|
|
213
|
+
},
|
|
214
|
+
async startDevServer(targetDir) {
|
|
215
|
+
const result = await runProcess(npmCmd(), ["run", "dev"], { cwd: targetDir });
|
|
216
|
+
if (result.code !== 0) {
|
|
217
|
+
throw new Error(`Dashboard dev server exited with code ${result.code}.`);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
};
|
|
221
|
+
async function writeDashboardEnv(targetDir, env, force) {
|
|
222
|
+
const envPath = join(targetDir, ".env.local");
|
|
223
|
+
if (existsSync(envPath) && !force) {
|
|
224
|
+
return { path: envPath, wrote: false };
|
|
225
|
+
}
|
|
226
|
+
const content = [
|
|
227
|
+
`NEXT_PUBLIC_SUPABASE_URL=${env.supabaseUrl}`,
|
|
228
|
+
`NEXT_PUBLIC_SUPABASE_ANON_KEY=${env.anonKey}`,
|
|
229
|
+
""
|
|
230
|
+
].join("\n");
|
|
231
|
+
await writeFile(envPath, content, "utf-8");
|
|
232
|
+
return { path: envPath, wrote: true };
|
|
233
|
+
}
|
|
234
|
+
async function runDashboardStart(options, ops = defaultOps) {
|
|
235
|
+
const targetDir = resolve(process.cwd(), options.dir);
|
|
236
|
+
console.log(pc.bold("Forcefield Dashboard Bootstrap"));
|
|
237
|
+
console.log(`Target: ${pc.cyan(targetDir)}`);
|
|
238
|
+
await ops.scaffoldDashboard(targetDir, options.force);
|
|
239
|
+
const env = options.local ? await ops.getLocalEnv() : {
|
|
240
|
+
supabaseUrl: PRODUCTION_SUPABASE_URL,
|
|
241
|
+
anonKey: PRODUCTION_SUPABASE_PUBLISHABLE_KEY
|
|
242
|
+
};
|
|
243
|
+
const envWrite = await writeDashboardEnv(targetDir, env, options.force);
|
|
244
|
+
if (envWrite.wrote) {
|
|
245
|
+
console.log(`${pc.green("\u2713")} Wrote ${pc.cyan(envWrite.path)} (${options.local ? "local" : "hosted"} backend).`);
|
|
246
|
+
} else {
|
|
247
|
+
console.log(`${pc.yellow("\u2022")} Preserved existing ${pc.cyan(envWrite.path)} (use ${pc.cyan("--force")} to overwrite).`);
|
|
248
|
+
}
|
|
249
|
+
if (options.install) {
|
|
250
|
+
console.log(`${pc.dim("\u2192")} Installing dashboard dependencies...`);
|
|
251
|
+
await ops.installDependencies(targetDir);
|
|
252
|
+
} else {
|
|
253
|
+
console.log(`${pc.yellow("\u2022")} Skipped dependency install (${pc.cyan("--no-install")}).`);
|
|
254
|
+
}
|
|
255
|
+
if (options.run) {
|
|
256
|
+
console.log(`${pc.dim("\u2192")} Starting dashboard dev server...`);
|
|
257
|
+
console.log(pc.dim("Press Ctrl+C to stop."));
|
|
258
|
+
await ops.startDevServer(targetDir);
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
261
|
+
console.log(`${pc.yellow("\u2022")} Skipped dev server startup (${pc.cyan("--no-run")}).`);
|
|
262
|
+
console.log("");
|
|
263
|
+
console.log("Next steps:");
|
|
264
|
+
console.log(` cd ${targetDir}`);
|
|
265
|
+
if (!options.install) {
|
|
266
|
+
console.log(" npm install");
|
|
267
|
+
}
|
|
268
|
+
console.log(" npm run dev");
|
|
269
|
+
console.log("");
|
|
270
|
+
console.log("Then open http://localhost:3000 and sign in.");
|
|
271
|
+
}
|
|
272
|
+
async function runDashboardCli(args) {
|
|
273
|
+
const parsed = parseDashboardCommand(args);
|
|
274
|
+
if (parsed.type === "help") {
|
|
275
|
+
if (parsed.invalidCommand) {
|
|
276
|
+
console.error(pc.red(`Unknown dashboard subcommand: ${parsed.invalidCommand}`));
|
|
277
|
+
}
|
|
278
|
+
if (parsed.parseError) {
|
|
279
|
+
console.error(pc.red(parsed.parseError));
|
|
280
|
+
}
|
|
281
|
+
printUsage();
|
|
282
|
+
if (parsed.invalidCommand || parsed.parseError) {
|
|
283
|
+
process.exitCode = 1;
|
|
284
|
+
}
|
|
285
|
+
return;
|
|
286
|
+
}
|
|
287
|
+
await runDashboardStart(parsed.options);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
// cli/index.ts
|
|
12
291
|
function parseForcefieldCommand(argv) {
|
|
13
292
|
const cmd = argv[2];
|
|
14
293
|
const rest = argv.slice(3);
|
|
15
294
|
if (!cmd || cmd === "setup") return { command: "setup", rest };
|
|
16
295
|
if (cmd === "doctor") return { command: "doctor", rest };
|
|
17
296
|
if (cmd === "status") return { command: "status", rest };
|
|
297
|
+
if (cmd === "dashboard") return { command: "dashboard", rest };
|
|
18
298
|
if (cmd === "mcp") return { command: "mcp", rest };
|
|
19
299
|
if (cmd === "help" || cmd === "--help" || cmd === "-h") return { command: "help", rest };
|
|
20
300
|
return { command: "help", rest: [cmd, ...rest] };
|
|
21
301
|
}
|
|
22
|
-
function
|
|
302
|
+
function printUsage2() {
|
|
23
303
|
console.log(
|
|
24
304
|
[
|
|
25
305
|
"Forcefield CLI",
|
|
@@ -28,11 +308,13 @@ function printUsage() {
|
|
|
28
308
|
" forcefield setup Run interactive setup wizard",
|
|
29
309
|
" forcefield doctor Validate setup + MCP connectivity",
|
|
30
310
|
" forcefield status Show setup status in current project",
|
|
311
|
+
" forcefield dashboard start Scaffold + run local dashboard (hosted backend by default)",
|
|
31
312
|
" forcefield mcp Start MCP server process (stdio)",
|
|
32
313
|
" forcefield help Show this help",
|
|
33
314
|
"",
|
|
34
315
|
"One-off without global install:",
|
|
35
|
-
" npx -y forcefield setup",
|
|
316
|
+
" npx -y @forcefield/forcefield setup",
|
|
317
|
+
" npx -y @forcefield/forcefield dashboard start",
|
|
36
318
|
"",
|
|
37
319
|
"Direct runtime package path (equivalent):",
|
|
38
320
|
" npx -y -p @forcefield/mcp-server forcefield setup"
|
|
@@ -42,7 +324,7 @@ function printUsage() {
|
|
|
42
324
|
async function runForcefieldCli(argv = process.argv) {
|
|
43
325
|
const { command, rest } = parseForcefieldCommand(argv);
|
|
44
326
|
if (command === "help") {
|
|
45
|
-
|
|
327
|
+
printUsage2();
|
|
46
328
|
if (rest.length > 0) {
|
|
47
329
|
process.exitCode = 1;
|
|
48
330
|
}
|
|
@@ -52,6 +334,10 @@ async function runForcefieldCli(argv = process.argv) {
|
|
|
52
334
|
await runMcpCli();
|
|
53
335
|
return;
|
|
54
336
|
}
|
|
337
|
+
if (command === "dashboard") {
|
|
338
|
+
await runDashboardCli(rest);
|
|
339
|
+
return;
|
|
340
|
+
}
|
|
55
341
|
const setupArgv = ["node", "forcefield-setup"];
|
|
56
342
|
if (command === "doctor") setupArgv.push("--doctor");
|
|
57
343
|
if (command === "status") setupArgv.push("--status");
|
package/build/cli/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../cli/index.ts"],"sourcesContent":["/**\n * Unified Forcefield CLI entrypoint.\n *\n * Gives users a single command surface:\n * forcefield setup\n * forcefield doctor\n * forcefield status\n * forcefield mcp\n */\n\nimport { pathToFileURL } from 'node:url';\nimport { runSetupCli } from '../setup/index.js';\nimport { runMcpCli } from '../src/index.js';\n\ntype ForcefieldCommand = 'setup' | 'doctor' | 'status' | 'mcp' | 'help';\n\nexport function parseForcefieldCommand(argv: string[]): { command: ForcefieldCommand; rest: string[] } {\n const cmd = argv[2];\n const rest = argv.slice(3);\n\n if (!cmd || cmd === 'setup') return { command: 'setup', rest };\n if (cmd === 'doctor') return { command: 'doctor', rest };\n if (cmd === 'status') return { command: 'status', rest };\n if (cmd === 'mcp') return { command: 'mcp', rest };\n if (cmd === 'help' || cmd === '--help' || cmd === '-h') return { command: 'help', rest };\n\n return { command: 'help', rest: [cmd, ...rest] };\n}\n\nfunction printUsage(): void {\n console.log(\n [\n 'Forcefield CLI',\n '',\n 'Usage:',\n ' forcefield setup Run interactive setup wizard',\n ' forcefield doctor Validate setup + MCP connectivity',\n ' forcefield status Show setup status in current project',\n ' forcefield mcp Start MCP server process (stdio)',\n ' forcefield help Show this help',\n '',\n 'One-off without global install:',\n ' npx -y forcefield setup',\n '',\n 'Direct runtime package path (equivalent):',\n ' npx -y -p @forcefield/mcp-server forcefield setup',\n ].join('\\n'),\n );\n}\n\nexport async function runForcefieldCli(argv: string[] = process.argv): Promise<void> {\n const { command, rest } = parseForcefieldCommand(argv);\n\n if (command === 'help') {\n printUsage();\n if (rest.length > 0) {\n process.exitCode = 1;\n }\n return;\n }\n\n if (command === 'mcp') {\n await runMcpCli();\n return;\n }\n\n const setupArgv = ['node', 'forcefield-setup'];\n if (command === 'doctor') setupArgv.push('--doctor');\n if (command === 'status') setupArgv.push('--status');\n setupArgv.push(...rest);\n await runSetupCli(setupArgv);\n}\n\nfunction isExecutedDirectly(metaUrl: string): boolean {\n const entryPath = process.argv[1];\n if (!entryPath) return false;\n return metaUrl === pathToFileURL(entryPath).href;\n}\n\nif (isExecutedDirectly(import.meta.url)) {\n void runForcefieldCli();\n}\n"],"mappings":";;;;;;;;;;AAUA,SAAS,qBAAqB;AAMvB,SAAS,uBAAuB,MAAgE;AACrG,QAAM,MAAM,KAAK,CAAC;AAClB,QAAM,OAAO,KAAK,MAAM,CAAC;AAEzB,MAAI,CAAC,OAAO,QAAQ,QAAS,QAAO,EAAE,SAAS,SAAS,KAAK;AAC7D,MAAI,QAAQ,SAAU,QAAO,EAAE,SAAS,UAAU,KAAK;AACvD,MAAI,QAAQ,SAAU,QAAO,EAAE,SAAS,UAAU,KAAK;AACvD,MAAI,QAAQ,MAAO,QAAO,EAAE,SAAS,OAAO,KAAK;AACjD,MAAI,QAAQ,UAAU,QAAQ,YAAY,QAAQ,KAAM,QAAO,EAAE,SAAS,QAAQ,KAAK;AAEvF,SAAO,EAAE,SAAS,QAAQ,MAAM,CAAC,KAAK,GAAG,IAAI,EAAE;AACjD;AAEA,SAAS,aAAmB;AAC1B,UAAQ;AAAA,IACN;AAAA,MACE;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF,EAAE,KAAK,IAAI;AAAA,EACb;AACF;AAEA,eAAsB,iBAAiB,OAAiB,QAAQ,MAAqB;AACnF,QAAM,EAAE,SAAS,KAAK,IAAI,uBAAuB,IAAI;AAErD,MAAI,YAAY,QAAQ;AACtB,eAAW;AACX,QAAI,KAAK,SAAS,GAAG;AACnB,cAAQ,WAAW;AAAA,IACrB;AACA;AAAA,EACF;AAEA,MAAI,YAAY,OAAO;AACrB,UAAM,UAAU;AAChB;AAAA,EACF;AAEA,QAAM,YAAY,CAAC,QAAQ,kBAAkB;AAC7C,MAAI,YAAY,SAAU,WAAU,KAAK,UAAU;AACnD,MAAI,YAAY,SAAU,WAAU,KAAK,UAAU;AACnD,YAAU,KAAK,GAAG,IAAI;AACtB,QAAM,YAAY,SAAS;AAC7B;AAEA,SAAS,mBAAmB,SAA0B;AACpD,QAAM,YAAY,QAAQ,KAAK,CAAC;AAChC,MAAI,CAAC,UAAW,QAAO;AACvB,SAAO,YAAY,cAAc,SAAS,EAAE;AAC9C;AAEA,IAAI,mBAAmB,YAAY,GAAG,GAAG;AACvC,OAAK,iBAAiB;AACxB;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../cli/index.ts","../../cli/dashboard.ts"],"sourcesContent":["/**\n * Unified Forcefield CLI entrypoint.\n *\n * Gives users a single command surface:\n * forcefield setup\n * forcefield doctor\n * forcefield status\n * forcefield dashboard start\n * forcefield mcp\n */\n\nimport { pathToFileURL } from 'node:url';\nimport { runSetupCli } from '../setup/index.js';\nimport { runMcpCli } from '../src/index.js';\nimport { runDashboardCli } from './dashboard.js';\n\ntype ForcefieldCommand = 'setup' | 'doctor' | 'status' | 'dashboard' | 'mcp' | 'help';\n\nexport function parseForcefieldCommand(argv: string[]): { command: ForcefieldCommand; rest: string[] } {\n const cmd = argv[2];\n const rest = argv.slice(3);\n\n if (!cmd || cmd === 'setup') return { command: 'setup', rest };\n if (cmd === 'doctor') return { command: 'doctor', rest };\n if (cmd === 'status') return { command: 'status', rest };\n if (cmd === 'dashboard') return { command: 'dashboard', rest };\n if (cmd === 'mcp') return { command: 'mcp', rest };\n if (cmd === 'help' || cmd === '--help' || cmd === '-h') return { command: 'help', rest };\n\n return { command: 'help', rest: [cmd, ...rest] };\n}\n\nfunction printUsage(): void {\n console.log(\n [\n 'Forcefield CLI',\n '',\n 'Usage:',\n ' forcefield setup Run interactive setup wizard',\n ' forcefield doctor Validate setup + MCP connectivity',\n ' forcefield status Show setup status in current project',\n ' forcefield dashboard start Scaffold + run local dashboard (hosted backend by default)',\n ' forcefield mcp Start MCP server process (stdio)',\n ' forcefield help Show this help',\n '',\n 'One-off without global install:',\n ' npx -y @forcefield/forcefield setup',\n ' npx -y @forcefield/forcefield dashboard start',\n '',\n 'Direct runtime package path (equivalent):',\n ' npx -y -p @forcefield/mcp-server forcefield setup',\n ].join('\\n'),\n );\n}\n\nexport async function runForcefieldCli(argv: string[] = process.argv): Promise<void> {\n const { command, rest } = parseForcefieldCommand(argv);\n\n if (command === 'help') {\n printUsage();\n if (rest.length > 0) {\n process.exitCode = 1;\n }\n return;\n }\n\n if (command === 'mcp') {\n await runMcpCli();\n return;\n }\n\n if (command === 'dashboard') {\n await runDashboardCli(rest);\n return;\n }\n\n const setupArgv = ['node', 'forcefield-setup'];\n if (command === 'doctor') setupArgv.push('--doctor');\n if (command === 'status') setupArgv.push('--status');\n setupArgv.push(...rest);\n await runSetupCli(setupArgv);\n}\n\nfunction isExecutedDirectly(metaUrl: string): boolean {\n const entryPath = process.argv[1];\n if (!entryPath) return false;\n return metaUrl === pathToFileURL(entryPath).href;\n}\n\nif (isExecutedDirectly(import.meta.url)) {\n void runForcefieldCli();\n}\n","import { spawn } from 'node:child_process';\nimport { existsSync } from 'node:fs';\nimport { writeFile } from 'node:fs/promises';\nimport { join, resolve } from 'node:path';\nimport pc from 'picocolors';\nimport {\n LOCAL_SUPABASE_URL,\n PRODUCTION_SUPABASE_PUBLISHABLE_KEY,\n PRODUCTION_SUPABASE_URL,\n} from '../src/supabase-defaults.js';\n\nconst DEFAULT_DIR = './forcefield-dashboard';\n\nexport interface DashboardStartOptions {\n dir: string;\n local: boolean;\n force: boolean;\n install: boolean;\n run: boolean;\n}\n\ninterface DashboardCommand {\n type: 'start' | 'help';\n options: DashboardStartOptions;\n invalidCommand?: string;\n parseError?: string;\n}\n\ninterface DashboardEnv {\n supabaseUrl: string;\n anonKey: string;\n}\n\ninterface DashboardOps {\n scaffoldDashboard(targetDir: string, force: boolean): Promise<void>;\n getLocalEnv(): Promise<DashboardEnv>;\n installDependencies(targetDir: string): Promise<void>;\n startDevServer(targetDir: string): Promise<void>;\n}\n\nfunction npmCmd(): string {\n return process.platform === 'win32' ? 'npm.cmd' : 'npm';\n}\n\nfunction printUsage(): void {\n console.log(\n [\n 'Forcefield Dashboard',\n '',\n 'Usage:',\n ' forcefield dashboard start [--dir <path>] [--local] [--force] [--no-install] [--no-run]',\n ' forcefield dashboard --help',\n '',\n 'Examples:',\n ' forcefield dashboard start',\n ' forcefield dashboard start --dir ./dashboard',\n ' forcefield dashboard start --local --force',\n ].join('\\n'),\n );\n}\n\nexport function parseDashboardCommand(args: string[]): DashboardCommand {\n const defaultOptions: DashboardStartOptions = {\n dir: DEFAULT_DIR,\n local: false,\n force: false,\n install: true,\n run: true,\n };\n\n if (args.includes('--help') || args.includes('-h')) {\n return { type: 'help', options: defaultOptions };\n }\n\n const first = args[0];\n const isStart = !first || first === 'start';\n if (!isStart) {\n return {\n type: 'help',\n invalidCommand: first,\n options: defaultOptions,\n };\n }\n\n const optionArgs = first === 'start' ? args.slice(1) : args;\n const options = { ...defaultOptions };\n\n for (let i = 0; i < optionArgs.length; i += 1) {\n const arg = optionArgs[i];\n if (arg === '--dir' || arg === '-d') {\n const value = optionArgs[i + 1];\n if (!value || value.startsWith('-')) {\n return {\n type: 'help',\n parseError: 'Missing value for --dir.',\n options: defaultOptions,\n };\n }\n options.dir = value;\n i += 1;\n continue;\n }\n\n if (arg === '--local') {\n options.local = true;\n continue;\n }\n\n if (arg === '--force' || arg === '-f') {\n options.force = true;\n continue;\n }\n\n if (arg === '--no-install') {\n options.install = false;\n continue;\n }\n\n if (arg === '--no-run') {\n options.run = false;\n continue;\n }\n\n return {\n type: 'help',\n parseError: `Unknown option: ${arg}`,\n options: defaultOptions,\n };\n }\n\n return {\n type: 'start',\n options,\n };\n}\n\nasync function runProcess(\n command: string,\n args: string[],\n options?: { cwd?: string; capture?: boolean },\n): Promise<{ code: number; stdout: string; stderr: string }> {\n return new Promise((resolvePromise, rejectPromise) => {\n const child = spawn(command, args, {\n cwd: options?.cwd,\n env: process.env,\n stdio: options?.capture ? ['ignore', 'pipe', 'pipe'] : 'inherit',\n });\n\n let stdout = '';\n let stderr = '';\n\n if (options?.capture) {\n child.stdout.on('data', (chunk) => {\n stdout += String(chunk);\n });\n child.stderr.on('data', (chunk) => {\n stderr += String(chunk);\n });\n }\n\n child.on('error', (error) => {\n rejectPromise(error);\n });\n\n child.on('close', (code) => {\n resolvePromise({ code: code ?? 1, stdout, stderr });\n });\n });\n}\n\nfunction parseEnvAssignments(raw: string): Record<string, string> {\n const env: Record<string, string> = {};\n for (const line of raw.split(/\\r?\\n/)) {\n const trimmed = line.trim();\n if (!trimmed || trimmed.startsWith('#')) continue;\n const match = trimmed.match(/^(?:export\\s+)?([A-Za-z_][A-Za-z0-9_]*)=(.*)$/);\n if (!match) continue;\n const key = match[1]!;\n let value = match[2] ?? '';\n if (\n (value.startsWith('\"') && value.endsWith('\"')) ||\n (value.startsWith(\"'\") && value.endsWith(\"'\"))\n ) {\n value = value.slice(1, -1);\n }\n env[key] = value;\n }\n return env;\n}\n\nfunction formatManualFallback(targetDir: string, force: boolean): string {\n return [\n 'Manual fallback:',\n ` npx -y @forcefield/dashboard-template@beta init --dir ${targetDir}${force ? ' --force' : ''}`,\n ].join('\\n');\n}\n\nconst defaultOps: DashboardOps = {\n async scaffoldDashboard(targetDir, force) {\n const args = [\n 'exec',\n '--yes',\n '--package=@forcefield/dashboard-template@beta',\n 'forcefield-dashboard',\n '--',\n 'init',\n '--dir',\n targetDir,\n ];\n if (force) {\n args.push('--force');\n }\n\n try {\n const result = await runProcess(npmCmd(), args);\n if (result.code !== 0) {\n throw new Error(`Scaffold command exited with code ${result.code}.`);\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n throw new Error(\n `Failed to scaffold dashboard automatically: ${message}\\n${formatManualFallback(targetDir, force)}`,\n );\n }\n },\n\n async getLocalEnv() {\n let result;\n try {\n result = await runProcess('supabase', ['status', '-o', 'env'], { capture: true });\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n throw new Error(\n `Could not run 'supabase status -o env': ${message}. Install Supabase CLI and run ${pc.cyan('supabase start')}, or remove ${pc.cyan('--local')}.`,\n );\n }\n\n if (result.code !== 0) {\n const detail = (result.stderr || result.stdout).trim() || `exit code ${result.code}`;\n throw new Error(\n `Local Supabase is unavailable (${detail}). Run ${pc.cyan('supabase start')} first, or remove ${pc.cyan('--local')}.`,\n );\n }\n\n const env = parseEnvAssignments(result.stdout);\n const supabaseUrl = env.SUPABASE_URL || LOCAL_SUPABASE_URL;\n const anonKey = env.SUPABASE_ANON_KEY || env.ANON_KEY;\n\n if (!anonKey) {\n throw new Error(\n `Could not find an anon key from ${pc.cyan('supabase status -o env')}. Ensure local stack is running, or remove ${pc.cyan('--local')}.`,\n );\n }\n\n return { supabaseUrl, anonKey };\n },\n\n async installDependencies(targetDir) {\n const result = await runProcess(npmCmd(), ['install'], { cwd: targetDir });\n if (result.code !== 0) {\n throw new Error(`Dependency install failed (exit ${result.code}).`);\n }\n },\n\n async startDevServer(targetDir) {\n const result = await runProcess(npmCmd(), ['run', 'dev'], { cwd: targetDir });\n if (result.code !== 0) {\n throw new Error(`Dashboard dev server exited with code ${result.code}.`);\n }\n },\n};\n\nasync function writeDashboardEnv(\n targetDir: string,\n env: DashboardEnv,\n force: boolean,\n): Promise<{ path: string; wrote: boolean }> {\n const envPath = join(targetDir, '.env.local');\n\n if (existsSync(envPath) && !force) {\n return { path: envPath, wrote: false };\n }\n\n const content = [\n `NEXT_PUBLIC_SUPABASE_URL=${env.supabaseUrl}`,\n `NEXT_PUBLIC_SUPABASE_ANON_KEY=${env.anonKey}`,\n '',\n ].join('\\n');\n\n await writeFile(envPath, content, 'utf-8');\n return { path: envPath, wrote: true };\n}\n\nexport async function runDashboardStart(\n options: DashboardStartOptions,\n ops: DashboardOps = defaultOps,\n): Promise<void> {\n const targetDir = resolve(process.cwd(), options.dir);\n\n console.log(pc.bold('Forcefield Dashboard Bootstrap'));\n console.log(`Target: ${pc.cyan(targetDir)}`);\n\n await ops.scaffoldDashboard(targetDir, options.force);\n\n const env = options.local\n ? await ops.getLocalEnv()\n : {\n supabaseUrl: PRODUCTION_SUPABASE_URL,\n anonKey: PRODUCTION_SUPABASE_PUBLISHABLE_KEY,\n };\n\n const envWrite = await writeDashboardEnv(targetDir, env, options.force);\n if (envWrite.wrote) {\n console.log(`${pc.green('✓')} Wrote ${pc.cyan(envWrite.path)} (${options.local ? 'local' : 'hosted'} backend).`);\n } else {\n console.log(`${pc.yellow('•')} Preserved existing ${pc.cyan(envWrite.path)} (use ${pc.cyan('--force')} to overwrite).`);\n }\n\n if (options.install) {\n console.log(`${pc.dim('→')} Installing dashboard dependencies...`);\n await ops.installDependencies(targetDir);\n } else {\n console.log(`${pc.yellow('•')} Skipped dependency install (${pc.cyan('--no-install')}).`);\n }\n\n if (options.run) {\n console.log(`${pc.dim('→')} Starting dashboard dev server...`);\n console.log(pc.dim('Press Ctrl+C to stop.'));\n await ops.startDevServer(targetDir);\n return;\n }\n\n console.log(`${pc.yellow('•')} Skipped dev server startup (${pc.cyan('--no-run')}).`);\n console.log('');\n console.log('Next steps:');\n console.log(` cd ${targetDir}`);\n if (!options.install) {\n console.log(' npm install');\n }\n console.log(' npm run dev');\n console.log('');\n console.log('Then open http://localhost:3000 and sign in.');\n}\n\nexport async function runDashboardCli(args: string[]): Promise<void> {\n const parsed = parseDashboardCommand(args);\n\n if (parsed.type === 'help') {\n if (parsed.invalidCommand) {\n console.error(pc.red(`Unknown dashboard subcommand: ${parsed.invalidCommand}`));\n }\n if (parsed.parseError) {\n console.error(pc.red(parsed.parseError));\n }\n printUsage();\n if (parsed.invalidCommand || parsed.parseError) {\n process.exitCode = 1;\n }\n return;\n }\n\n await runDashboardStart(parsed.options);\n}\n\nexport { parseEnvAssignments };\n"],"mappings":"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","names":["printUsage"]}
|
package/build/index.js
CHANGED
package/build/setup/index.js
CHANGED
package/package.json
CHANGED
package/workflows/ff-help.md
CHANGED
|
@@ -116,7 +116,7 @@ Based on the compliance state, suggest the most relevant next action:
|
|
|
116
116
|
- This workflow has **no prerequisites** — it works even before onboarding
|
|
117
117
|
- Primary entrypoint is `/ff-onboard`. `/ff-start` is optional for troubleshooting/connection checks.
|
|
118
118
|
- If `ff_system(action: "health")` fails, show the command reference only and suggest:
|
|
119
|
-
1. run `forcefield
|
|
119
|
+
1. run `forcefield setup` in terminal
|
|
120
120
|
2. restart/reload coding agent chat
|
|
121
121
|
3. run `/mcp` in Claude Code and confirm `forcefield` is connected
|
|
122
122
|
- Check whether suggested commands are implemented before showing them. If a command is not yet available, append "(coming soon)" to its description
|
package/workflows/ff-start.md
CHANGED
|
@@ -36,7 +36,7 @@ Call `ff_system(action: "health")`.
|
|
|
36
36
|
If health fails, show this exact recovery flow:
|
|
37
37
|
|
|
38
38
|
1. "Open a terminal in your project repo."
|
|
39
|
-
2. "Run `forcefield
|
|
39
|
+
2. "Run `forcefield setup`."
|
|
40
40
|
3. "Restart/reload your coding agent chat."
|
|
41
41
|
4. "In Claude Code, run `/mcp` and confirm `forcefield` is connected."
|
|
42
42
|
5. "Run `/ff-start` again."
|