draftify-cli 1.0.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/README.md ADDED
@@ -0,0 +1,47 @@
1
+ # Draftify CLI
2
+
3
+ Draftify CLI is a command-line interface for interacting with Draftify AI directly from your terminal. It allows you to seamlessly refactor files, chat with an AI assistant in your project, and much more.
4
+
5
+ ## Installation
6
+
7
+ You can install the CLI globally using npm:
8
+
9
+ ```bash
10
+ npm install -g draftify-cli
11
+ ```
12
+
13
+ ## Usage
14
+
15
+ ### Getting Started
16
+
17
+ If you simply type `draftify` into your terminal, it will automatically initiate the login flow or start the interactive REPL if you are already authenticated.
18
+
19
+ ```bash
20
+ draftify
21
+ ```
22
+
23
+ ### Commands
24
+
25
+ - `draftify login`
26
+ Authenticates your terminal session with the Draftify Website. You will be redirected to the web browser to confirm login securely.
27
+
28
+ - `draftify refactor <file> -i <instruction>`
29
+ Refactor a specific file using Draftify AI based on the provided instruction.
30
+
31
+ **Example:**
32
+ ```bash
33
+ draftify refactor src/index.ts -i "Add error handling to all promises"
34
+ ```
35
+
36
+ ## Security & Privacy
37
+
38
+ The CLI is completely key-less. It communicates with the Draftify Website using short-lived session tokens. Your API keys remain securely on your local server/website configuration and are never exposed directly to the CLI or saved on the local filesystem of your projects.
39
+
40
+ ## Development
41
+
42
+ To build the CLI locally:
43
+
44
+ 1. Clone the repository
45
+ 2. Run `npm install`
46
+ 3. Run `npm run build`
47
+ 4. You can link it locally using `npm link`
@@ -0,0 +1,70 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.loginCommand = loginCommand;
7
+ const open_1 = __importDefault(require("open"));
8
+ const crypto_1 = __importDefault(require("crypto"));
9
+ const axios_1 = __importDefault(require("axios"));
10
+ const config_1 = require("../utils/config");
11
+ const ui_1 = require("../utils/ui");
12
+ const kleur_1 = require("kleur");
13
+ // Default Draftify URL, can be overridden by env
14
+ const DRAFTIFY_WEB_URL = process.env.DRAFTIFY_WEB_URL || "http://localhost:3000";
15
+ async function loginCommand() {
16
+ const code = crypto_1.default.randomBytes(4).toString("hex");
17
+ const authUrl = `${DRAFTIFY_WEB_URL}/auth/cli?code=${code}`;
18
+ ui_1.ui.blank();
19
+ ui_1.ui.box("Draftify Authentication", [
20
+ "To continue, we need to authenticate this device.",
21
+ "",
22
+ "If your browser doesn't open automatically, please visit:",
23
+ (0, kleur_1.bold)((0, kleur_1.yellow)(authUrl)),
24
+ "",
25
+ `Your device code is: ${(0, kleur_1.bold)(code)}`,
26
+ "",
27
+ (0, kleur_1.dim)("Waiting for authorization...")
28
+ ]);
29
+ ui_1.ui.blank();
30
+ try {
31
+ await (0, open_1.default)(authUrl);
32
+ }
33
+ catch (error) {
34
+ // Ignore open errors, user can click the link manually
35
+ }
36
+ // Polling logic
37
+ const pollInterval = 2000; // 2 seconds
38
+ const maxAttempts = 150; // 5 minutes timeout
39
+ let attempts = 0;
40
+ return new Promise((resolve, reject) => {
41
+ const intervalId = setInterval(async () => {
42
+ attempts++;
43
+ if (attempts > maxAttempts) {
44
+ clearInterval(intervalId);
45
+ ui_1.ui.error("Authentication timed out. Please try running '/login' again.");
46
+ process.exit(1);
47
+ }
48
+ try {
49
+ const response = await axios_1.default.post(`${DRAFTIFY_WEB_URL}/api/cli/poll`, { code });
50
+ if (response.data && response.data.status === "AUTHORIZED" && response.data.token) {
51
+ clearInterval(intervalId);
52
+ // Save the token
53
+ (0, config_1.saveConfig)({ token: response.data.token });
54
+ const username = response.data.username || "Developer";
55
+ resolve(username);
56
+ }
57
+ // If status is PENDING, we just continue polling
58
+ }
59
+ catch (err) {
60
+ // Ignore network errors during polling (e.g. server restarting), just keep trying
61
+ // but if we get a persistent 400 error, we should exit.
62
+ if (err.response && err.response.status === 400) {
63
+ clearInterval(intervalId);
64
+ ui_1.ui.error("Invalid request during polling.");
65
+ process.exit(1);
66
+ }
67
+ }
68
+ }, pollInterval);
69
+ });
70
+ }
@@ -0,0 +1,64 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.refactorCommand = refactorCommand;
7
+ const fs_1 = __importDefault(require("fs"));
8
+ const path_1 = __importDefault(require("path"));
9
+ const ui_1 = require("../utils/ui");
10
+ const config_1 = require("../utils/config");
11
+ const api_1 = require("../utils/api");
12
+ const MAX_FILE_SIZE_BYTES = 500 * 1024; // 500 KB
13
+ async function refactorCommand(file, options) {
14
+ ui_1.ui.header("Draftify CLI");
15
+ const token = (0, config_1.getToken)();
16
+ if (!token) {
17
+ ui_1.ui.error("No CLI token found. Please run 'draftify login' first.");
18
+ process.exit(1);
19
+ }
20
+ const instruction = options.instruction?.trim();
21
+ if (!instruction) {
22
+ ui_1.ui.error("Instruction is required. Use -i or --instruction flag.");
23
+ process.exit(1);
24
+ }
25
+ const absolutePath = path_1.default.resolve(process.cwd(), file);
26
+ // Validate File Exists
27
+ if (!fs_1.default.existsSync(absolutePath)) {
28
+ ui_1.ui.error(`File not found: ${absolutePath}`);
29
+ process.exit(1);
30
+ }
31
+ // Validate File Size
32
+ const stat = fs_1.default.statSync(absolutePath);
33
+ if (stat.size > MAX_FILE_SIZE_BYTES) {
34
+ ui_1.ui.error(`File is too large (${Math.round(stat.size / 1024)} KB). Maximum allowed size is 500 KB.`);
35
+ process.exit(1);
36
+ }
37
+ ui_1.ui.step(`Target : ${path_1.default.basename(absolutePath)}`);
38
+ ui_1.ui.step(`Action : ${instruction}`);
39
+ ui_1.ui.blank();
40
+ let code;
41
+ try {
42
+ code = fs_1.default.readFileSync(absolutePath, "utf-8");
43
+ }
44
+ catch (error) {
45
+ ui_1.ui.error(`Failed to read file: ${error.message}`);
46
+ process.exit(1);
47
+ }
48
+ const spinner = (0, ui_1.createSpinner)("Analyzing and refactoring...").start();
49
+ try {
50
+ const result = await (0, api_1.refactorCodeApi)(path_1.default.basename(file), code, instruction, [], (0, config_1.getModel)(), [], // empty skills array
51
+ "Medium" // default thinking level
52
+ );
53
+ spinner.stop();
54
+ ui_1.ui.success("Refactoring complete");
55
+ ui_1.ui.divider();
56
+ console.log(result);
57
+ ui_1.ui.divider();
58
+ }
59
+ catch (error) {
60
+ spinner.stop();
61
+ ui_1.ui.error(error.message);
62
+ process.exit(1);
63
+ }
64
+ }
package/dist/index.js ADDED
@@ -0,0 +1,51 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ const commander_1 = require("commander");
5
+ const login_1 = require("./commands/login");
6
+ const refactor_1 = require("./commands/refactor");
7
+ const repl_1 = require("./repl");
8
+ const ui_1 = require("./utils/ui");
9
+ const config_1 = require("./utils/config");
10
+ const program = new commander_1.Command();
11
+ program
12
+ .name("draftify")
13
+ .description("Draftify AI Command Line Interface")
14
+ .version("1.0.0");
15
+ program
16
+ .command("login")
17
+ .description("Authenticate with your Draftify CLI token")
18
+ .action(async () => {
19
+ await (0, login_1.loginCommand)();
20
+ });
21
+ program
22
+ .command("refactor <file>")
23
+ .description("Refactor a specific file using Draftify AI")
24
+ .requiredOption("-i, --instruction <text>", "Instruction for the AI on how to refactor the file")
25
+ .action(async (file, options) => {
26
+ await (0, refactor_1.refactorCommand)(file, options);
27
+ });
28
+ // Handle unknown commands
29
+ program.on('command:*', function () {
30
+ ui_1.ui.error(`Invalid command: ${program.args.join(' ')}\nSee --help for a list of available commands.`);
31
+ process.exit(1);
32
+ });
33
+ // Custom logic for when `draftify` is run without any arguments
34
+ if (process.argv.slice(2).length === 0) {
35
+ const token = (0, config_1.getToken)();
36
+ if (!token) {
37
+ // No token, run interactive login flow
38
+ (0, login_1.loginCommand)().then((username) => (0, repl_1.startRepl)(username)).catch(() => process.exit(1));
39
+ }
40
+ else {
41
+ // Token exists, start REPL
42
+ (0, repl_1.startRepl)().catch((err) => {
43
+ ui_1.ui.error(`REPL crashed: ${err.message}`);
44
+ process.exit(1);
45
+ });
46
+ }
47
+ }
48
+ else {
49
+ // Otherwise, let commander handle the parsed args
50
+ program.parse(process.argv);
51
+ }