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 +47 -0
- package/dist/commands/login.js +70 -0
- package/dist/commands/refactor.js +64 -0
- package/dist/index.js +51 -0
- package/dist/repl.js +1102 -0
- package/dist/utils/api.js +106 -0
- package/dist/utils/chats.js +52 -0
- package/dist/utils/config.js +73 -0
- package/dist/utils/skills.js +45 -0
- package/dist/utils/ui.js +135 -0
- package/package.json +39 -0
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.refactorCodeApi = refactorCodeApi;
|
|
4
|
+
exports.getUserProfile = getUserProfile;
|
|
5
|
+
const config_1 = require("./config");
|
|
6
|
+
async function refactorCodeApi(fileName, code, instruction, history, modelName, skillsData, thinkingLevel, onChunk, abortSignal) {
|
|
7
|
+
const token = (0, config_1.getToken)();
|
|
8
|
+
if (!token) {
|
|
9
|
+
throw new Error("No token found. Please run 'draftify login' first.");
|
|
10
|
+
}
|
|
11
|
+
const apiUrl = (0, config_1.getApiUrl)();
|
|
12
|
+
const response = await fetch(apiUrl, {
|
|
13
|
+
method: "POST",
|
|
14
|
+
headers: {
|
|
15
|
+
"Content-Type": "application/json",
|
|
16
|
+
Authorization: `Bearer ${token}`
|
|
17
|
+
},
|
|
18
|
+
body: JSON.stringify({ fileName, code, instruction, history, modelName, skillsData, thinkingLevel }),
|
|
19
|
+
signal: abortSignal
|
|
20
|
+
});
|
|
21
|
+
if (!response.ok) {
|
|
22
|
+
const errorText = await response.text();
|
|
23
|
+
let errorMsg = errorText;
|
|
24
|
+
try {
|
|
25
|
+
const errorJson = JSON.parse(errorText);
|
|
26
|
+
errorMsg = errorJson.error || errorText;
|
|
27
|
+
}
|
|
28
|
+
catch { }
|
|
29
|
+
const status = response.status;
|
|
30
|
+
if (status === 401)
|
|
31
|
+
throw new Error("Unauthorized: Invalid or expired CLI token. Please login again.");
|
|
32
|
+
if (status === 403)
|
|
33
|
+
throw new Error(`Forbidden: ${errorMsg}`);
|
|
34
|
+
if (status === 429)
|
|
35
|
+
throw new Error(`Rate Limit Exceeded: ${errorMsg}`);
|
|
36
|
+
throw new Error(`Server Error (${status}): ${errorMsg}`);
|
|
37
|
+
}
|
|
38
|
+
const reader = response.body?.getReader();
|
|
39
|
+
if (!reader)
|
|
40
|
+
throw new Error("No response body from server");
|
|
41
|
+
const decoder = new TextDecoder("utf-8");
|
|
42
|
+
let buffer = "";
|
|
43
|
+
let fullResult = "";
|
|
44
|
+
while (true) {
|
|
45
|
+
const { done, value } = await reader.read();
|
|
46
|
+
if (done)
|
|
47
|
+
break;
|
|
48
|
+
buffer += decoder.decode(value, { stream: true });
|
|
49
|
+
let boundary = buffer.indexOf('\n');
|
|
50
|
+
while (boundary !== -1) {
|
|
51
|
+
const line = buffer.slice(0, boundary).trim();
|
|
52
|
+
buffer = buffer.slice(boundary + 1);
|
|
53
|
+
boundary = buffer.indexOf('\n');
|
|
54
|
+
if (line.startsWith("data: ") && line !== "data: [DONE]") {
|
|
55
|
+
try {
|
|
56
|
+
const dataObj = JSON.parse(line.slice(6));
|
|
57
|
+
let textChunk = dataObj.choices?.[0]?.delta?.content || "";
|
|
58
|
+
if (!textChunk && dataObj.candidates && dataObj.candidates.length > 0) {
|
|
59
|
+
textChunk = dataObj.candidates[0].content?.parts?.[0]?.text || "";
|
|
60
|
+
}
|
|
61
|
+
if (textChunk) {
|
|
62
|
+
fullResult += textChunk;
|
|
63
|
+
if (onChunk)
|
|
64
|
+
onChunk(textChunk);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
catch (e) {
|
|
68
|
+
// ignore incomplete json chunk parsing errors
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
else if (line.startsWith('{"result":')) {
|
|
72
|
+
// Fallback for non-streaming response
|
|
73
|
+
try {
|
|
74
|
+
const dataObj = JSON.parse(line);
|
|
75
|
+
if (dataObj.result) {
|
|
76
|
+
fullResult += dataObj.result;
|
|
77
|
+
if (onChunk)
|
|
78
|
+
onChunk(dataObj.result);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
catch (e) { }
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return fullResult;
|
|
86
|
+
}
|
|
87
|
+
async function getUserProfile() {
|
|
88
|
+
const token = (0, config_1.getToken)();
|
|
89
|
+
if (!token)
|
|
90
|
+
return null;
|
|
91
|
+
const apiUrl = (0, config_1.getApiUrl)();
|
|
92
|
+
try {
|
|
93
|
+
const response = await fetch(apiUrl, {
|
|
94
|
+
method: "GET",
|
|
95
|
+
headers: { Authorization: `Bearer ${token}` }
|
|
96
|
+
});
|
|
97
|
+
if (response.ok) {
|
|
98
|
+
const data = await response.json();
|
|
99
|
+
return { username: data.username, plan: data.plan };
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
catch (e) {
|
|
103
|
+
// Ignore errors
|
|
104
|
+
}
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
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.loadChats = loadChats;
|
|
7
|
+
exports.saveChats = saveChats;
|
|
8
|
+
exports.updateChatSession = updateChatSession;
|
|
9
|
+
const fs_1 = __importDefault(require("fs"));
|
|
10
|
+
const path_1 = __importDefault(require("path"));
|
|
11
|
+
const os_1 = __importDefault(require("os"));
|
|
12
|
+
const CONFIG_DIR = path_1.default.join(os_1.default.homedir(), ".config", "draftify");
|
|
13
|
+
const CHATS_FILE = path_1.default.join(CONFIG_DIR, "chats.json");
|
|
14
|
+
function loadChats() {
|
|
15
|
+
if (!fs_1.default.existsSync(CHATS_FILE)) {
|
|
16
|
+
return [];
|
|
17
|
+
}
|
|
18
|
+
try {
|
|
19
|
+
const raw = fs_1.default.readFileSync(CHATS_FILE, "utf-8");
|
|
20
|
+
return JSON.parse(raw);
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
return [];
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
function saveChats(chats) {
|
|
27
|
+
if (!fs_1.default.existsSync(CONFIG_DIR)) {
|
|
28
|
+
fs_1.default.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
29
|
+
}
|
|
30
|
+
fs_1.default.writeFileSync(CHATS_FILE, JSON.stringify(chats, null, 2), "utf-8");
|
|
31
|
+
}
|
|
32
|
+
function updateChatSession(id, title, model, history) {
|
|
33
|
+
const chats = loadChats();
|
|
34
|
+
const index = chats.findIndex(c => c.id === id);
|
|
35
|
+
// Clean title: use first 40 characters of first message or default
|
|
36
|
+
const cleanTitle = title.replace(/\n/g, " ").substring(0, 40) || "New Chat";
|
|
37
|
+
const updatedSession = {
|
|
38
|
+
id,
|
|
39
|
+
title: cleanTitle,
|
|
40
|
+
model,
|
|
41
|
+
history,
|
|
42
|
+
updatedAt: new Date().toISOString()
|
|
43
|
+
};
|
|
44
|
+
if (index !== -1) {
|
|
45
|
+
chats[index] = updatedSession;
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
chats.unshift(updatedSession); // New session at the top
|
|
49
|
+
}
|
|
50
|
+
// Keep last 20 chats
|
|
51
|
+
saveChats(chats.slice(0, 20));
|
|
52
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
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.getConfig = getConfig;
|
|
7
|
+
exports.saveConfig = saveConfig;
|
|
8
|
+
exports.getToken = getToken;
|
|
9
|
+
exports.getApiUrl = getApiUrl;
|
|
10
|
+
exports.getModel = getModel;
|
|
11
|
+
exports.setModel = setModel;
|
|
12
|
+
exports.getSkills = getSkills;
|
|
13
|
+
exports.setSkills = setSkills;
|
|
14
|
+
exports.getThinkingLevel = getThinkingLevel;
|
|
15
|
+
exports.setThinkingLevel = setThinkingLevel;
|
|
16
|
+
const os_1 = __importDefault(require("os"));
|
|
17
|
+
const fs_1 = __importDefault(require("fs"));
|
|
18
|
+
const path_1 = __importDefault(require("path"));
|
|
19
|
+
const CONFIG_DIR = path_1.default.join(os_1.default.homedir(), ".config", "draftify");
|
|
20
|
+
const CONFIG_FILE = path_1.default.join(CONFIG_DIR, "config.json");
|
|
21
|
+
function getConfig() {
|
|
22
|
+
if (!fs_1.default.existsSync(CONFIG_FILE)) {
|
|
23
|
+
return {};
|
|
24
|
+
}
|
|
25
|
+
try {
|
|
26
|
+
const raw = fs_1.default.readFileSync(CONFIG_FILE, "utf-8");
|
|
27
|
+
return JSON.parse(raw);
|
|
28
|
+
}
|
|
29
|
+
catch (error) {
|
|
30
|
+
return {};
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
function saveConfig(newConfig) {
|
|
34
|
+
const current = getConfig();
|
|
35
|
+
const merged = { ...current, ...newConfig };
|
|
36
|
+
if (!fs_1.default.existsSync(CONFIG_DIR)) {
|
|
37
|
+
fs_1.default.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
38
|
+
}
|
|
39
|
+
fs_1.default.writeFileSync(CONFIG_FILE, JSON.stringify(merged, null, 2), {
|
|
40
|
+
encoding: "utf-8",
|
|
41
|
+
mode: 0o600, // Secure permissions
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
function getToken() {
|
|
45
|
+
const config = getConfig();
|
|
46
|
+
return config.token || null;
|
|
47
|
+
}
|
|
48
|
+
function getApiUrl() {
|
|
49
|
+
const config = getConfig();
|
|
50
|
+
// Fallback API URL, overrideable via config or DRAFTIFY_API_URL
|
|
51
|
+
return process.env.DRAFTIFY_API_URL || config.apiUrl || "http://localhost:3000/api/cli";
|
|
52
|
+
}
|
|
53
|
+
function getModel() {
|
|
54
|
+
const config = getConfig();
|
|
55
|
+
return config.model || "Opus 4.7-level";
|
|
56
|
+
}
|
|
57
|
+
function setModel(modelName) {
|
|
58
|
+
saveConfig({ model: modelName });
|
|
59
|
+
}
|
|
60
|
+
function getSkills() {
|
|
61
|
+
const config = getConfig();
|
|
62
|
+
return config.skills || [];
|
|
63
|
+
}
|
|
64
|
+
function setSkills(skills) {
|
|
65
|
+
saveConfig({ skills });
|
|
66
|
+
}
|
|
67
|
+
function getThinkingLevel() {
|
|
68
|
+
const config = getConfig();
|
|
69
|
+
return config.thinkingLevel || 'Medium';
|
|
70
|
+
}
|
|
71
|
+
function setThinkingLevel(level) {
|
|
72
|
+
saveConfig({ thinkingLevel: level });
|
|
73
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
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.getAvailableSkills = getAvailableSkills;
|
|
7
|
+
const fs_1 = __importDefault(require("fs"));
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
const os_1 = __importDefault(require("os"));
|
|
10
|
+
function getAvailableSkills() {
|
|
11
|
+
const skills = [];
|
|
12
|
+
const globalSkillsDir = path_1.default.join(os_1.default.homedir(), ".agents", "skills");
|
|
13
|
+
const localSkillsDir = path_1.default.join(process.cwd(), ".agents", "skills");
|
|
14
|
+
const scanDir = (dir, source) => {
|
|
15
|
+
if (!fs_1.default.existsSync(dir))
|
|
16
|
+
return;
|
|
17
|
+
try {
|
|
18
|
+
const entries = fs_1.default.readdirSync(dir, { withFileTypes: true });
|
|
19
|
+
for (const entry of entries) {
|
|
20
|
+
if (entry.isDirectory()) {
|
|
21
|
+
const skillName = entry.name;
|
|
22
|
+
const skillMdPath = path_1.default.join(dir, skillName, "SKILL.md");
|
|
23
|
+
if (fs_1.default.existsSync(skillMdPath)) {
|
|
24
|
+
const content = fs_1.default.readFileSync(skillMdPath, "utf-8");
|
|
25
|
+
// Only add if not already added (local overrides global if same name)
|
|
26
|
+
if (!skills.some(s => s.name === skillName)) {
|
|
27
|
+
skills.push({
|
|
28
|
+
name: skillName,
|
|
29
|
+
content,
|
|
30
|
+
source
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
catch (e) {
|
|
38
|
+
// Ignore read errors
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
// Local first so it takes precedence over global
|
|
42
|
+
scanDir(localSkillsDir, "local");
|
|
43
|
+
scanDir(globalSkillsDir, "global");
|
|
44
|
+
return skills;
|
|
45
|
+
}
|
package/dist/utils/ui.js
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
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.ui = void 0;
|
|
7
|
+
exports.createSpinner = createSpinner;
|
|
8
|
+
const kleur_1 = require("kleur");
|
|
9
|
+
const ora_1 = __importDefault(require("ora"));
|
|
10
|
+
// Bento-style minimalist & Claude-style orange UI
|
|
11
|
+
exports.ui = {
|
|
12
|
+
header: (text) => console.log(`\n${(0, kleur_1.bold)(text)}`),
|
|
13
|
+
step: (text) => console.log(` ${(0, kleur_1.dim)("│")} ${text}`),
|
|
14
|
+
info: (text) => console.log(` ${(0, kleur_1.dim)("│")} ${(0, kleur_1.dim)(text)}`),
|
|
15
|
+
success: (text) => console.log(` ${(0, kleur_1.dim)("└")} ${(0, kleur_1.bold)(text)}\n`),
|
|
16
|
+
error: (text) => console.log(`\n ${(0, kleur_1.bold)("x")} ${text}\n`),
|
|
17
|
+
fileAction: (text) => {
|
|
18
|
+
const greenCheck = `\x1b[32m✓\x1b[0m`;
|
|
19
|
+
console.log(` ${greenCheck} ${text}`);
|
|
20
|
+
},
|
|
21
|
+
fileRead: (filePath) => {
|
|
22
|
+
const icon = `\x1b[36m›\x1b[0m`;
|
|
23
|
+
const gray = (s) => `\x1b[90m${s}\x1b[0m`;
|
|
24
|
+
console.log(` ${icon} ${gray("Reading:")} ${filePath}`);
|
|
25
|
+
},
|
|
26
|
+
fileCreate: (filePath) => {
|
|
27
|
+
const icon = `\x1b[32m+\x1b[0m`;
|
|
28
|
+
const gray = (s) => `\x1b[90m${s}\x1b[0m`;
|
|
29
|
+
console.log(` ${icon} ${gray("Creating:")} \x1b[32m${filePath}\x1b[0m`);
|
|
30
|
+
},
|
|
31
|
+
fileModify: (filePath) => {
|
|
32
|
+
const icon = `\x1b[33m~\x1b[0m`;
|
|
33
|
+
const gray = (s) => `\x1b[90m${s}\x1b[0m`;
|
|
34
|
+
console.log(` ${icon} ${gray("Modifying:")} \x1b[33m${filePath}\x1b[0m`);
|
|
35
|
+
},
|
|
36
|
+
fileDelete: (filePath) => {
|
|
37
|
+
const icon = `\x1b[31m-\x1b[0m`;
|
|
38
|
+
const gray = (s) => `\x1b[90m${s}\x1b[0m`;
|
|
39
|
+
console.log(` ${icon} ${gray("Deleting:")} \x1b[31m${filePath}\x1b[0m`);
|
|
40
|
+
},
|
|
41
|
+
fileSuccess: (action, filePath) => {
|
|
42
|
+
const check = `\x1b[32m✓\x1b[0m`;
|
|
43
|
+
console.log(` ${check} ${action}: ${filePath}`);
|
|
44
|
+
},
|
|
45
|
+
divider: () => console.log((0, kleur_1.dim)("──────────────────────────────────────────")),
|
|
46
|
+
blank: () => console.log(""),
|
|
47
|
+
// Claude Code style orange box with terminal-width-aware wrapping
|
|
48
|
+
box: (title, lines) => {
|
|
49
|
+
const terminalWidth = process.stdout.columns || 80;
|
|
50
|
+
const maxBoxWidth = Math.max(40, Math.min(100, terminalWidth - 8));
|
|
51
|
+
const stripAnsi = (str) => str.replace(/\u001b\[[0-9;]*m/g, '');
|
|
52
|
+
const processedLines = [];
|
|
53
|
+
for (const line of lines) {
|
|
54
|
+
const plainLength = stripAnsi(line).length;
|
|
55
|
+
if (plainLength <= maxBoxWidth) {
|
|
56
|
+
processedLines.push(line);
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
const words = line.split(" ");
|
|
60
|
+
let currentLine = "";
|
|
61
|
+
for (const word of words) {
|
|
62
|
+
const currentLinePlain = stripAnsi(currentLine).length;
|
|
63
|
+
const wordPlain = stripAnsi(word).length;
|
|
64
|
+
if (currentLinePlain + wordPlain + (currentLine ? 1 : 0) > maxBoxWidth) {
|
|
65
|
+
if (currentLine) {
|
|
66
|
+
processedLines.push(currentLine);
|
|
67
|
+
currentLine = word;
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
processedLines.push(word);
|
|
71
|
+
currentLine = "";
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
currentLine = currentLine ? currentLine + " " + word : word;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
if (currentLine) {
|
|
79
|
+
processedLines.push(currentLine);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
const width = Math.max(title.length + 4, ...processedLines.map(l => stripAnsi(l).length), 40);
|
|
84
|
+
console.log((0, kleur_1.yellow)(`╭─ ${title} ${"─".repeat(width - title.length - 3)}╮`));
|
|
85
|
+
processedLines.forEach(line => {
|
|
86
|
+
const plainLength = stripAnsi(line).length;
|
|
87
|
+
console.log((0, kleur_1.yellow)("│ ") + line + " ".repeat(Math.max(0, width - plainLength)) + (0, kleur_1.yellow)(" │"));
|
|
88
|
+
});
|
|
89
|
+
console.log((0, kleur_1.yellow)(`╰${"─".repeat(width + 2)}╯`));
|
|
90
|
+
},
|
|
91
|
+
// Minimalist status line for REPL
|
|
92
|
+
statusLine: (repoName, model) => {
|
|
93
|
+
const text = `Draftify Scale | Engine: ${model} | Repo: ${repoName}`;
|
|
94
|
+
console.log(`\n ${(0, kleur_1.dim)(`[${text}]`)}`);
|
|
95
|
+
},
|
|
96
|
+
welcomeScreen: (repoName, model, username, plan = "Draftify Scale", thinkingLevel = "Medium") => {
|
|
97
|
+
// Borderless Claude Code style with strong orange logo
|
|
98
|
+
const orange = (s) => `\x1b[38;5;208m${s}\x1b[0m`;
|
|
99
|
+
const gray = (s) => `\x1b[90m${s}\x1b[0m`;
|
|
100
|
+
const white = (s) => `\x1b[37m${s}\x1b[0m`;
|
|
101
|
+
console.log("");
|
|
102
|
+
const greeting = username ? `Welcome ${username} to Draftify Code!` : "Welcome to Draftify Code!";
|
|
103
|
+
console.log(` ${orange("█ █ █ █")} ${white(greeting)}`);
|
|
104
|
+
console.log(` ${orange("█████████")} ${white("Draftify Code ")}${gray("v1.0.0")}`);
|
|
105
|
+
console.log(` ${orange("█ █████ █")} ${gray(`${model} | Thinking: ${thinkingLevel} [${plan}]`)}`);
|
|
106
|
+
console.log(` ${orange("█████████")} ${gray(process.cwd())}`);
|
|
107
|
+
console.log(` ${orange(" █ █ ")}`);
|
|
108
|
+
console.log("");
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
function createSpinner(text) {
|
|
112
|
+
const startTime = Date.now();
|
|
113
|
+
let currentText = text;
|
|
114
|
+
const spinner = (0, ora_1.default)({
|
|
115
|
+
text: (0, kleur_1.dim)(`${currentText} 0.0s`),
|
|
116
|
+
spinner: {
|
|
117
|
+
interval: 120,
|
|
118
|
+
frames: ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"],
|
|
119
|
+
},
|
|
120
|
+
color: "white",
|
|
121
|
+
});
|
|
122
|
+
const timer = setInterval(() => {
|
|
123
|
+
const elapsed = ((Date.now() - startTime) / 1000).toFixed(1);
|
|
124
|
+
spinner.text = (0, kleur_1.dim)(`${currentText} ${elapsed}s`);
|
|
125
|
+
}, 100);
|
|
126
|
+
const originalStop = spinner.stop.bind(spinner);
|
|
127
|
+
spinner.stop = () => {
|
|
128
|
+
clearInterval(timer);
|
|
129
|
+
return originalStop();
|
|
130
|
+
};
|
|
131
|
+
spinner.setPrefix = (newText) => {
|
|
132
|
+
currentText = newText;
|
|
133
|
+
};
|
|
134
|
+
return spinner;
|
|
135
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "draftify-cli",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Draftify AI CLI tool",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"draftify": "dist/index.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"dist"
|
|
11
|
+
],
|
|
12
|
+
"scripts": {
|
|
13
|
+
"build": "tsc",
|
|
14
|
+
"start": "node dist/index.js",
|
|
15
|
+
"dev": "tsc --watch",
|
|
16
|
+
"prepublishOnly": "npm run build"
|
|
17
|
+
},
|
|
18
|
+
"keywords": [
|
|
19
|
+
"draftify",
|
|
20
|
+
"cli",
|
|
21
|
+
"ai",
|
|
22
|
+
"assistant"
|
|
23
|
+
],
|
|
24
|
+
"author": "",
|
|
25
|
+
"license": "ISC",
|
|
26
|
+
"dependencies": {
|
|
27
|
+
"ansi-escapes": "^7.3.0",
|
|
28
|
+
"axios": "^1.7.2",
|
|
29
|
+
"commander": "^12.1.0",
|
|
30
|
+
"enquirer": "^2.4.1",
|
|
31
|
+
"kleur": "^4.1.5",
|
|
32
|
+
"open": "^8.4.2",
|
|
33
|
+
"ora": "^5.4.1"
|
|
34
|
+
},
|
|
35
|
+
"devDependencies": {
|
|
36
|
+
"@types/node": "^20.14.2",
|
|
37
|
+
"typescript": "^5.4.5"
|
|
38
|
+
}
|
|
39
|
+
}
|