@fixprompt/cli 0.3.0 → 0.4.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/cli.js +105 -14
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -87,7 +87,7 @@ function detectContext() {
|
|
|
87
87
|
}
|
|
88
88
|
|
|
89
89
|
// src/connect.ts
|
|
90
|
-
import { readFileSync, readdirSync, statSync } from "fs";
|
|
90
|
+
import { appendFileSync, existsSync, readFileSync, readdirSync, statSync, writeFileSync } from "fs";
|
|
91
91
|
import { join } from "path";
|
|
92
92
|
import { spawnSync } from "child_process";
|
|
93
93
|
var DEFAULT_ENDPOINT = "https://geosloghub-production.up.railway.app";
|
|
@@ -255,6 +255,80 @@ function writeToProvider(envVar, token, provider, target, force, cwd) {
|
|
|
255
255
|
return writeVercel(envVar, token, target);
|
|
256
256
|
}
|
|
257
257
|
}
|
|
258
|
+
function ensureGitignoreHas(cwd, pattern) {
|
|
259
|
+
const gi = join(cwd, ".gitignore");
|
|
260
|
+
let s = "";
|
|
261
|
+
try {
|
|
262
|
+
s = readFileSync(gi, "utf8");
|
|
263
|
+
} catch {
|
|
264
|
+
}
|
|
265
|
+
const lines = s.split(/\r?\n/);
|
|
266
|
+
if (lines.some((l) => l.trim() === pattern)) return false;
|
|
267
|
+
const sep = s.length === 0 || s.endsWith("\n") ? "" : "\n";
|
|
268
|
+
writeFileSync(gi, s + sep + "\n# FixLoop\n" + pattern + "\n");
|
|
269
|
+
return true;
|
|
270
|
+
}
|
|
271
|
+
function appendEnvLocal(cwd, token, projectId, projectSlug, endpoint) {
|
|
272
|
+
const envPath = join(cwd, ".env.local");
|
|
273
|
+
const lines = [
|
|
274
|
+
"",
|
|
275
|
+
"# FixLoop \u2014 read token for the local coding agent. Gitignored.",
|
|
276
|
+
`# Generated ${(/* @__PURE__ */ new Date()).toISOString()}`,
|
|
277
|
+
`# Manage / revoke: https://fixloop-dashboard.vercel.app/integrations/${projectId}`,
|
|
278
|
+
`FIXPROMPT_PROJECT_ID=${projectId}`
|
|
279
|
+
];
|
|
280
|
+
if (projectSlug) lines.push(`FIXPROMPT_PROJECT_SLUG=${projectSlug}`);
|
|
281
|
+
lines.push(`FIXPROMPT_READ_TOKEN=${token}`);
|
|
282
|
+
lines.push(`FIXPROMPT_BROKER_URL=${endpoint}`);
|
|
283
|
+
lines.push("");
|
|
284
|
+
const block = lines.join("\n");
|
|
285
|
+
if (existsSync(envPath)) {
|
|
286
|
+
appendFileSync(envPath, block);
|
|
287
|
+
} else {
|
|
288
|
+
writeFileSync(envPath, block.replace(/^\n/, ""));
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
function appendClaudeMdGuidance(cwd, projectId, projectSlug) {
|
|
292
|
+
const claudePath = join(cwd, "CLAUDE.md");
|
|
293
|
+
const section = [
|
|
294
|
+
"",
|
|
295
|
+
"## FixLoop \u2014 production error logs (auto-fetch on bug reports)",
|
|
296
|
+
"",
|
|
297
|
+
'This project ships errors to FixLoop. When the user reports a bug, crash, error, "X is broken", or pastes an error stack:',
|
|
298
|
+
"",
|
|
299
|
+
"1. Read the FixLoop env vars from `.env.local` (gitignored \u2014 never commit).",
|
|
300
|
+
"2. `curl` the broker for recent error lines.",
|
|
301
|
+
"3. Parse the response `entries[]` \u2014 each has `ts`, `level`, `message`, optional `attrs` + `stack`. Match against the user's symptom.",
|
|
302
|
+
"4. Propose a fix grounded in what the logs show. **Do not ask the user to paste log lines \u2014 fetch them yourself.**",
|
|
303
|
+
"",
|
|
304
|
+
"### Fetch logs",
|
|
305
|
+
"",
|
|
306
|
+
"```sh",
|
|
307
|
+
'curl -sH "x-fixprompt-read-token: $FIXPROMPT_READ_TOKEN" \\',
|
|
308
|
+
' "$FIXPROMPT_BROKER_URL/projects/$FIXPROMPT_PROJECT_ID/logs?since=24h&level=error,warn,fatal&limit=200"',
|
|
309
|
+
"```",
|
|
310
|
+
"",
|
|
311
|
+
"Query params: `since` (15m..7d), `level` (csv: debug,info,warn,error,fatal), `q` (substring), `limit` (1..500).",
|
|
312
|
+
"",
|
|
313
|
+
projectSlug ? `**Project**: \`${projectSlug}\`.` : "",
|
|
314
|
+
`Manage / revoke: https://fixloop-dashboard.vercel.app/integrations/${projectId}`,
|
|
315
|
+
""
|
|
316
|
+
].filter(Boolean).join("\n");
|
|
317
|
+
if (existsSync(claudePath)) {
|
|
318
|
+
const existing = readFileSync(claudePath, "utf8");
|
|
319
|
+
if (existing.includes("## FixLoop \u2014")) return;
|
|
320
|
+
appendFileSync(claudePath, section);
|
|
321
|
+
} else {
|
|
322
|
+
writeFileSync(claudePath, "# Project\n" + section);
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
function writeLocalDiscovery(opts) {
|
|
326
|
+
console.log(` \xB7 writing .env.local + CLAUDE.md guidance to ${opts.cwd}\u2026`);
|
|
327
|
+
const giChanged = ensureGitignoreHas(opts.cwd, ".env.local");
|
|
328
|
+
if (giChanged) console.log(" + appended .env.local to .gitignore");
|
|
329
|
+
appendEnvLocal(opts.cwd, opts.token, opts.projectId, opts.projectSlug, opts.endpoint);
|
|
330
|
+
appendClaudeMdGuidance(opts.cwd, opts.projectId, opts.projectSlug);
|
|
331
|
+
}
|
|
258
332
|
async function connect(args) {
|
|
259
333
|
const cwd = process.cwd();
|
|
260
334
|
const scopeRaw = (args.flags.scope ?? "deploy").toLowerCase();
|
|
@@ -272,7 +346,7 @@ async function connect(args) {
|
|
|
272
346
|
}
|
|
273
347
|
const endpoint = (args.flags.endpoint ?? process.env.FIXPROMPT_ENDPOINT ?? DEFAULT_ENDPOINT).replace(/\/$/, "");
|
|
274
348
|
let projectId = args.flags["project-id"] ?? process.env.FIXPROMPT_PROJECT_ID ?? null;
|
|
275
|
-
|
|
349
|
+
let projectSlug = args.flags["project-slug"] ?? null;
|
|
276
350
|
if (!projectId && projectSlug) {
|
|
277
351
|
const res = await fetch(`${endpoint}/admin/projects/by-slug/${encodeURIComponent(projectSlug)}`, {
|
|
278
352
|
headers: { "x-internal-admin-token": adminToken }
|
|
@@ -284,6 +358,7 @@ async function connect(args) {
|
|
|
284
358
|
}
|
|
285
359
|
const parsed = JSON.parse(text);
|
|
286
360
|
projectId = parsed.id;
|
|
361
|
+
projectSlug = parsed.slug;
|
|
287
362
|
console.log(` resolved --project-slug=${projectSlug} \u2192 ${parsed.id} (${parsed.name})`);
|
|
288
363
|
}
|
|
289
364
|
if (!projectId) {
|
|
@@ -293,8 +368,9 @@ async function connect(args) {
|
|
|
293
368
|
process.exit(1);
|
|
294
369
|
}
|
|
295
370
|
const explicit = args.flags.provider;
|
|
296
|
-
const
|
|
297
|
-
|
|
371
|
+
const detectedProvider = explicit ?? detectProvider(cwd);
|
|
372
|
+
const scopeNeedsProvider = scope !== "read";
|
|
373
|
+
if (scopeNeedsProvider && !detectedProvider) {
|
|
298
374
|
console.error(
|
|
299
375
|
`Couldn't detect a provider from ${cwd}. Pass --provider <eas|vercel|github-actions>.
|
|
300
376
|
Detection looks for: app.json/eas.json (EAS), .vercel/project.json (Vercel),
|
|
@@ -302,8 +378,10 @@ async function connect(args) {
|
|
|
302
378
|
);
|
|
303
379
|
process.exit(1);
|
|
304
380
|
}
|
|
381
|
+
const provider = detectedProvider;
|
|
305
382
|
const target = {
|
|
306
|
-
provider,
|
|
383
|
+
provider: provider ?? "eas",
|
|
384
|
+
// unused when provider is null; placeholder
|
|
307
385
|
easEnv: args.flags["eas-env"] ?? "production",
|
|
308
386
|
vercelProjectId: args.flags["vercel-project-id"] ?? void 0,
|
|
309
387
|
vercelTargets: typeof args.flags["vercel-targets"] === "string" ? args.flags["vercel-targets"].split(",").map((t) => t.trim()) : void 0
|
|
@@ -311,7 +389,7 @@ async function connect(args) {
|
|
|
311
389
|
const labelBase = args.flags.label ?? `${provider}-${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").slice(0, 19)}`;
|
|
312
390
|
console.log(`fixprompt connect`);
|
|
313
391
|
console.log(` scope : ${scope}`);
|
|
314
|
-
console.log(` provider : ${provider}`);
|
|
392
|
+
console.log(` provider : ${provider ?? "(none \u2014 read-only, writing local files)"}`);
|
|
315
393
|
console.log(` project_id : ${projectId}`);
|
|
316
394
|
console.log(` endpoint : ${endpoint}`);
|
|
317
395
|
console.log(` label : ${labelBase}`);
|
|
@@ -326,12 +404,24 @@ async function connect(args) {
|
|
|
326
404
|
\u2022 Minting ${s} token via broker\u2026`);
|
|
327
405
|
const minted = await mintToken({ endpoint, adminToken, projectId, label, scope: s });
|
|
328
406
|
console.log(` \u2713 token_id ${minted.token_id}`);
|
|
329
|
-
|
|
407
|
+
if (s === "read") {
|
|
408
|
+
writeLocalDiscovery({
|
|
409
|
+
cwd,
|
|
410
|
+
token: minted.token,
|
|
411
|
+
projectId,
|
|
412
|
+
projectSlug,
|
|
413
|
+
endpoint
|
|
414
|
+
});
|
|
415
|
+
} else if (provider) {
|
|
416
|
+
await writeToProvider(envVar, minted.token, provider, target, force, cwd);
|
|
417
|
+
}
|
|
330
418
|
mintedTokens.push({ scope: s, token_id: minted.token_id, envVar });
|
|
331
419
|
}
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
420
|
+
const readMinted = mintedTokens.some((m) => m.scope === "read");
|
|
421
|
+
const deployMinted = mintedTokens.some((m) => m.scope === "deploy");
|
|
422
|
+
console.log("");
|
|
423
|
+
if (deployMinted && provider) {
|
|
424
|
+
console.log(`\u2713 FIXPROMPT_DEPLOY_TOKEN set on ${provider}.`);
|
|
335
425
|
if (provider === "eas") {
|
|
336
426
|
console.log(` Next OTA: npm run release:ota --branch=production --message="..."`);
|
|
337
427
|
} else if (provider === "github-actions") {
|
|
@@ -340,9 +430,10 @@ async function connect(args) {
|
|
|
340
430
|
console.log(` The deploy token is in your Vercel project env.`);
|
|
341
431
|
}
|
|
342
432
|
}
|
|
343
|
-
if (
|
|
344
|
-
console.log(
|
|
345
|
-
console.log(`
|
|
433
|
+
if (readMinted) {
|
|
434
|
+
console.log(`\u2713 FIXPROMPT_READ_TOKEN written to .env.local; CLAUDE.md updated.`);
|
|
435
|
+
console.log(` Coding agents (Claude Code / Cursor / Copilot) opening this repo will see`);
|
|
436
|
+
console.log(` the FixLoop section in CLAUDE.md and know to fetch logs on bug reports.`);
|
|
346
437
|
}
|
|
347
438
|
console.log(`
|
|
348
439
|
Revoke any of these from the dashboard \u2192 Integrations.`);
|
|
@@ -353,7 +444,7 @@ async function connect(args) {
|
|
|
353
444
|
|
|
354
445
|
// src/version.ts
|
|
355
446
|
var CLI_NAME = "@fixprompt/cli";
|
|
356
|
-
var CLI_VERSION = "0.
|
|
447
|
+
var CLI_VERSION = "0.4.0";
|
|
357
448
|
|
|
358
449
|
// src/cli.ts
|
|
359
450
|
var DEFAULT_ENDPOINT2 = "https://geosloghub-production.up.railway.app";
|
package/package.json
CHANGED