prdforge-cli 0.1.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/.env.example +9 -0
- package/README.md +129 -0
- package/bin/prdforge +2 -0
- package/package.json +57 -0
- package/src/api/client.js +164 -0
- package/src/api/client.test.js +34 -0
- package/src/commands/auth.js +160 -0
- package/src/commands/bulk.js +126 -0
- package/src/commands/config.js +58 -0
- package/src/commands/credits.js +21 -0
- package/src/commands/dashboard.js +88 -0
- package/src/commands/export.js +67 -0
- package/src/commands/generate.js +117 -0
- package/src/commands/mcp.js +100 -0
- package/src/commands/prd.js +221 -0
- package/src/commands/watch.js +65 -0
- package/src/commands/whoami.js +24 -0
- package/src/index.js +65 -0
- package/src/ui/Dashboard.js +263 -0
- package/src/ui/PrdCreation.js +66 -0
- package/src/ui/PromptBox.js +129 -0
- package/src/ui/StageIndicator.js +40 -0
- package/src/ui/theme.js +21 -0
- package/src/utils/config.js +45 -0
- package/src/utils/config.test.js +40 -0
- package/src/utils/output.js +61 -0
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import Conf from "conf";
|
|
2
|
+
|
|
3
|
+
export const config = new Conf({
|
|
4
|
+
projectName: "prdforge",
|
|
5
|
+
schema: {
|
|
6
|
+
apiKey: { type: "string", default: "" },
|
|
7
|
+
email: { type: "string", default: "" },
|
|
8
|
+
apiUrl: { type: "string", default: "https://jnlkzcmeiksqljnbtfhb.supabase.co/functions/v1" },
|
|
9
|
+
defaultFormat: { type: "string", default: "markdown" },
|
|
10
|
+
defaultProject: { type: "string", default: "" },
|
|
11
|
+
outputDir: { type: "string", default: "./prd-exports" },
|
|
12
|
+
},
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
export function getApiKey() {
|
|
16
|
+
return process.env.PRDFORGE_API_KEY || config.get("apiKey");
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function getApiUrl() {
|
|
20
|
+
return process.env.PRDFORGE_API_URL || config.get("apiUrl");
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function getEmail() {
|
|
24
|
+
return process.env.PRDFORGE_EMAIL || config.get("email");
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function setEmail(email) {
|
|
28
|
+
config.set("email", email);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** Derives the cli-auth endpoint URL from the configured API URL. */
|
|
32
|
+
export function getAuthBase() {
|
|
33
|
+
return getApiUrl().replace(/\/prdforge-api\/?$/, "/cli-auth");
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function requireApiKey() {
|
|
37
|
+
const key = getApiKey();
|
|
38
|
+
if (!key) {
|
|
39
|
+
console.error(
|
|
40
|
+
"\x1b[31m✖\x1b[0m No API key set. Run \x1b[36mprdforge auth login\x1b[0m to authenticate."
|
|
41
|
+
);
|
|
42
|
+
process.exit(1);
|
|
43
|
+
}
|
|
44
|
+
return key;
|
|
45
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { describe, it, beforeEach, afterEach } from "node:test";
|
|
2
|
+
import assert from "node:assert";
|
|
3
|
+
import { getApiKey, getApiUrl } from "./config.js";
|
|
4
|
+
|
|
5
|
+
describe("config", () => {
|
|
6
|
+
const origApiKey = process.env.PRDFORGE_API_KEY;
|
|
7
|
+
const origApiUrl = process.env.PRDFORGE_API_URL;
|
|
8
|
+
|
|
9
|
+
afterEach(() => {
|
|
10
|
+
if (origApiKey !== undefined) process.env.PRDFORGE_API_KEY = origApiKey;
|
|
11
|
+
else delete process.env.PRDFORGE_API_KEY;
|
|
12
|
+
if (origApiUrl !== undefined) process.env.PRDFORGE_API_URL = origApiUrl;
|
|
13
|
+
else delete process.env.PRDFORGE_API_URL;
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
describe("getApiKey", () => {
|
|
17
|
+
it("returns PRDFORGE_API_KEY when set", () => {
|
|
18
|
+
process.env.PRDFORGE_API_KEY = "env-key-12345";
|
|
19
|
+
assert.strictEqual(getApiKey(), "env-key-12345");
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it("returns a string when PRDFORGE_API_KEY is unset", () => {
|
|
23
|
+
delete process.env.PRDFORGE_API_KEY;
|
|
24
|
+
assert.strictEqual(typeof getApiKey(), "string");
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
describe("getApiUrl", () => {
|
|
29
|
+
it("returns PRDFORGE_API_URL when set", () => {
|
|
30
|
+
process.env.PRDFORGE_API_URL = "https://custom.example.com/v1";
|
|
31
|
+
assert.strictEqual(getApiUrl(), "https://custom.example.com/v1");
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it("returns a string when PRDFORGE_API_URL is unset", () => {
|
|
35
|
+
delete process.env.PRDFORGE_API_URL;
|
|
36
|
+
assert.strictEqual(typeof getApiUrl(), "string");
|
|
37
|
+
assert.ok(getApiUrl().length > 0);
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
});
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import Table from "cli-table3";
|
|
2
|
+
|
|
3
|
+
const C = {
|
|
4
|
+
reset: "\x1b[0m",
|
|
5
|
+
bold: "\x1b[1m",
|
|
6
|
+
dim: "\x1b[2m",
|
|
7
|
+
green: "\x1b[32m",
|
|
8
|
+
red: "\x1b[31m",
|
|
9
|
+
yellow: "\x1b[33m",
|
|
10
|
+
blue: "\x1b[34m",
|
|
11
|
+
cyan: "\x1b[36m",
|
|
12
|
+
magenta: "\x1b[35m",
|
|
13
|
+
gray: "\x1b[90m",
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export const color = C;
|
|
17
|
+
|
|
18
|
+
export function success(msg) {
|
|
19
|
+
console.log(`${C.green}✔${C.reset} ${msg}`);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function error(msg) {
|
|
23
|
+
console.error(`${C.red}✖${C.reset} ${msg}`);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function warn(msg) {
|
|
27
|
+
console.warn(`${C.yellow}⚠${C.reset} ${msg}`);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function info(msg) {
|
|
31
|
+
console.log(`${C.blue}ℹ${C.reset} ${msg}`);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function dim(msg) {
|
|
35
|
+
console.log(`${C.gray}${msg}${C.reset}`);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function header(title) {
|
|
39
|
+
const line = "─".repeat(Math.min(process.stdout.columns || 60, 60));
|
|
40
|
+
console.log(`\n${C.bold}${C.cyan}${title}${C.reset}`);
|
|
41
|
+
console.log(`${C.gray}${line}${C.reset}`);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function table(columns, rows) {
|
|
45
|
+
const t = new Table({
|
|
46
|
+
head: columns.map((c) => `${C.bold}${C.cyan}${c}${C.reset}`),
|
|
47
|
+
style: { border: ["gray"], head: [] },
|
|
48
|
+
});
|
|
49
|
+
rows.forEach((r) => t.push(r));
|
|
50
|
+
console.log(t.toString());
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function json(obj) {
|
|
54
|
+
console.log(JSON.stringify(obj, null, 2));
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function printPRDSection(section) {
|
|
58
|
+
console.log(`\n${C.bold}${C.blue}## ${section.title}${C.reset}`);
|
|
59
|
+
console.log(`${C.gray}Type: ${section.section_type}${C.reset}`);
|
|
60
|
+
console.log(`\n${section.content}\n`);
|
|
61
|
+
}
|