devwing 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 +1 -1
- package/dist/index.js +909 -97
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { Command } from 'commander';
|
|
3
|
-
import
|
|
4
|
-
import
|
|
3
|
+
import chalk7 from 'chalk';
|
|
4
|
+
import inquirer5 from 'inquirer';
|
|
5
5
|
import axios2 from 'axios';
|
|
6
6
|
import Conf from 'conf';
|
|
7
7
|
import { AsyncEntry } from '@napi-rs/keyring';
|
|
@@ -401,37 +401,37 @@ var Logger = class {
|
|
|
401
401
|
* Success message
|
|
402
402
|
*/
|
|
403
403
|
success(message) {
|
|
404
|
-
console.log(
|
|
404
|
+
console.log(chalk7.green("\u2713"), message);
|
|
405
405
|
}
|
|
406
406
|
/**
|
|
407
407
|
* Error message
|
|
408
408
|
*/
|
|
409
409
|
error(message, error) {
|
|
410
|
-
console.log(
|
|
410
|
+
console.log(chalk7.red("\u2717"), message);
|
|
411
411
|
if (error && process.env.DEBUG) {
|
|
412
|
-
console.error(
|
|
412
|
+
console.error(chalk7.gray(error.stack || error));
|
|
413
413
|
}
|
|
414
414
|
}
|
|
415
415
|
/**
|
|
416
416
|
* Warning message
|
|
417
417
|
*/
|
|
418
418
|
warn(message) {
|
|
419
|
-
console.log(
|
|
419
|
+
console.log(chalk7.yellow("\u26A0"), message);
|
|
420
420
|
}
|
|
421
421
|
/**
|
|
422
422
|
* Info message
|
|
423
423
|
*/
|
|
424
424
|
info(message) {
|
|
425
|
-
console.log(
|
|
425
|
+
console.log(chalk7.blue("\u2139"), message);
|
|
426
426
|
}
|
|
427
427
|
/**
|
|
428
428
|
* Debug message (only shown if DEBUG env var is set)
|
|
429
429
|
*/
|
|
430
430
|
debug(message, data) {
|
|
431
431
|
if (process.env.DEBUG) {
|
|
432
|
-
console.log(
|
|
432
|
+
console.log(chalk7.gray("\u2699"), chalk7.gray(message));
|
|
433
433
|
if (data) {
|
|
434
|
-
console.log(
|
|
434
|
+
console.log(chalk7.gray(JSON.stringify(data, null, 2)));
|
|
435
435
|
}
|
|
436
436
|
}
|
|
437
437
|
}
|
|
@@ -530,7 +530,7 @@ async function loginCommand() {
|
|
|
530
530
|
try {
|
|
531
531
|
const existingKey = await configManager.getApiKey();
|
|
532
532
|
if (existingKey) {
|
|
533
|
-
const { overwrite } = await
|
|
533
|
+
const { overwrite } = await inquirer5.prompt([
|
|
534
534
|
{
|
|
535
535
|
type: "confirm",
|
|
536
536
|
name: "overwrite",
|
|
@@ -545,7 +545,7 @@ async function loginCommand() {
|
|
|
545
545
|
}
|
|
546
546
|
logger.printBanner();
|
|
547
547
|
logger.newline();
|
|
548
|
-
const { method } = await
|
|
548
|
+
const { method } = await inquirer5.prompt([
|
|
549
549
|
{
|
|
550
550
|
type: "list",
|
|
551
551
|
name: "method",
|
|
@@ -573,7 +573,7 @@ async function loginCommand() {
|
|
|
573
573
|
}
|
|
574
574
|
}
|
|
575
575
|
async function loginWithEmail() {
|
|
576
|
-
const { email, password } = await
|
|
576
|
+
const { email, password } = await inquirer5.prompt([
|
|
577
577
|
{
|
|
578
578
|
type: "input",
|
|
579
579
|
name: "email",
|
|
@@ -604,7 +604,7 @@ async function loginWithEmail() {
|
|
|
604
604
|
if (loginResult.requires_2fa) {
|
|
605
605
|
logger.succeedSpinner("Credentials verified");
|
|
606
606
|
logger.info("Two-factor authentication is enabled on your account");
|
|
607
|
-
const { totpCode } = await
|
|
607
|
+
const { totpCode } = await inquirer5.prompt([
|
|
608
608
|
{
|
|
609
609
|
type: "input",
|
|
610
610
|
name: "totpCode",
|
|
@@ -657,10 +657,10 @@ async function loginWithBrowser() {
|
|
|
657
657
|
logger.success("Browser opened! Please log in to authorize this device.");
|
|
658
658
|
} catch {
|
|
659
659
|
logger.warn("Could not open browser automatically.");
|
|
660
|
-
logger.info(`Please visit: ${
|
|
660
|
+
logger.info(`Please visit: ${chalk7.cyan(verifyUrl)}`);
|
|
661
661
|
}
|
|
662
662
|
logger.newline();
|
|
663
|
-
logger.info(
|
|
663
|
+
logger.info(chalk7.dim(`Device code: ${initResponse.user_code.substring(0, 8)}...`));
|
|
664
664
|
logger.startSpinner("Waiting for authorization...");
|
|
665
665
|
const pollInterval = (initResponse.interval || 5) * 1e3;
|
|
666
666
|
const maxAttempts = Math.floor((initResponse.expires_in || 900) / (initResponse.interval || 5));
|
|
@@ -719,7 +719,7 @@ async function loginWithApiKey() {
|
|
|
719
719
|
);
|
|
720
720
|
logger.info(`Get your API key from the ${dashboardUrl}`);
|
|
721
721
|
logger.newline();
|
|
722
|
-
const { apiKey } = await
|
|
722
|
+
const { apiKey } = await inquirer5.prompt([
|
|
723
723
|
{
|
|
724
724
|
type: "password",
|
|
725
725
|
name: "apiKey",
|
|
@@ -759,7 +759,7 @@ async function logoutCommand() {
|
|
|
759
759
|
logger.info("You are not logged in");
|
|
760
760
|
return;
|
|
761
761
|
}
|
|
762
|
-
const { confirm } = await
|
|
762
|
+
const { confirm } = await inquirer5.prompt([
|
|
763
763
|
{
|
|
764
764
|
type: "confirm",
|
|
765
765
|
name: "confirm",
|
|
@@ -796,15 +796,15 @@ async function statusCommand() {
|
|
|
796
796
|
const plan = getUserPlan(user);
|
|
797
797
|
logger.succeedSpinner("Authenticated");
|
|
798
798
|
logger.newline();
|
|
799
|
-
console.log(
|
|
799
|
+
console.log(chalk7.bold("User Profile:"));
|
|
800
800
|
console.log(` Email: ${user.email}`);
|
|
801
801
|
console.log(` Name: ${getUserDisplayName(user)}`);
|
|
802
802
|
console.log(` Plan: ${plan.toUpperCase()}`);
|
|
803
803
|
if (user.is_verified !== void 0) {
|
|
804
|
-
console.log(` Verified: ${user.is_verified ?
|
|
804
|
+
console.log(` Verified: ${user.is_verified ? chalk7.green("Yes") : chalk7.yellow("No")}`);
|
|
805
805
|
}
|
|
806
806
|
if (user.totp_enabled !== void 0) {
|
|
807
|
-
console.log(` 2FA: ${user.totp_enabled ?
|
|
807
|
+
console.log(` 2FA: ${user.totp_enabled ? chalk7.green("Enabled") : chalk7.dim("Disabled")}`);
|
|
808
808
|
}
|
|
809
809
|
const config = configManager.getAll();
|
|
810
810
|
if (config.workspaceId) {
|
|
@@ -1399,22 +1399,22 @@ Summary: ${result.summary.changes} changes, ${result.summary.insertions} inserti
|
|
|
1399
1399
|
// src/streaming/renderer.ts
|
|
1400
1400
|
marked.setOptions({
|
|
1401
1401
|
renderer: new TerminalRenderer({
|
|
1402
|
-
code:
|
|
1403
|
-
blockquote:
|
|
1404
|
-
html:
|
|
1405
|
-
heading:
|
|
1406
|
-
firstHeading:
|
|
1407
|
-
hr:
|
|
1408
|
-
listitem:
|
|
1409
|
-
list:
|
|
1410
|
-
table:
|
|
1411
|
-
paragraph:
|
|
1412
|
-
strong:
|
|
1413
|
-
em:
|
|
1414
|
-
codespan:
|
|
1415
|
-
del:
|
|
1416
|
-
link:
|
|
1417
|
-
href:
|
|
1402
|
+
code: chalk7.cyan,
|
|
1403
|
+
blockquote: chalk7.gray.italic,
|
|
1404
|
+
html: chalk7.gray,
|
|
1405
|
+
heading: chalk7.bold.underline,
|
|
1406
|
+
firstHeading: chalk7.bold.magenta,
|
|
1407
|
+
hr: chalk7.reset,
|
|
1408
|
+
listitem: chalk7.reset,
|
|
1409
|
+
list: chalk7.reset,
|
|
1410
|
+
table: chalk7.reset,
|
|
1411
|
+
paragraph: chalk7.reset,
|
|
1412
|
+
strong: chalk7.bold,
|
|
1413
|
+
em: chalk7.italic,
|
|
1414
|
+
codespan: chalk7.cyan,
|
|
1415
|
+
del: chalk7.dim.strikethrough,
|
|
1416
|
+
link: chalk7.blue.underline,
|
|
1417
|
+
href: chalk7.blue.underline
|
|
1418
1418
|
})
|
|
1419
1419
|
});
|
|
1420
1420
|
var StreamingRenderer = class {
|
|
@@ -1509,10 +1509,10 @@ var StreamingRenderer = class {
|
|
|
1509
1509
|
logger.newline();
|
|
1510
1510
|
const { tool, args } = message;
|
|
1511
1511
|
if (!tool) return;
|
|
1512
|
-
logger.info(
|
|
1512
|
+
logger.info(chalk7.yellow(`AI requested tool: ${chalk7.bold(tool)}`));
|
|
1513
1513
|
if (args && Object.keys(args).length > 0) {
|
|
1514
|
-
console.log(
|
|
1515
|
-
console.log(
|
|
1514
|
+
console.log(chalk7.gray("Arguments:"));
|
|
1515
|
+
console.log(chalk7.gray(JSON.stringify(args, null, 2)));
|
|
1516
1516
|
}
|
|
1517
1517
|
logger.newline();
|
|
1518
1518
|
}
|
|
@@ -1524,11 +1524,11 @@ var StreamingRenderer = class {
|
|
|
1524
1524
|
const results = [];
|
|
1525
1525
|
for (const { tool, args } of this.pendingToolCalls) {
|
|
1526
1526
|
logger.newline();
|
|
1527
|
-
logger.info(
|
|
1527
|
+
logger.info(chalk7.yellow(`Executing ${chalk7.bold(tool)}...`));
|
|
1528
1528
|
const requiresConfirmation = this.requiresConfirmation(tool);
|
|
1529
1529
|
let confirmed = true;
|
|
1530
1530
|
if (requiresConfirmation) {
|
|
1531
|
-
const answers = await
|
|
1531
|
+
const answers = await inquirer5.prompt([
|
|
1532
1532
|
{
|
|
1533
1533
|
type: "confirm",
|
|
1534
1534
|
name: "confirmed",
|
|
@@ -1557,9 +1557,9 @@ var StreamingRenderer = class {
|
|
|
1557
1557
|
const content = result.output || "";
|
|
1558
1558
|
const preview = content.substring(0, 500);
|
|
1559
1559
|
if (preview.length < content.length) {
|
|
1560
|
-
console.log(
|
|
1560
|
+
console.log(chalk7.gray(preview + "... (truncated)"));
|
|
1561
1561
|
} else {
|
|
1562
|
-
console.log(
|
|
1562
|
+
console.log(chalk7.gray(preview));
|
|
1563
1563
|
}
|
|
1564
1564
|
results.push({
|
|
1565
1565
|
tool,
|
|
@@ -1597,9 +1597,9 @@ var StreamingRenderer = class {
|
|
|
1597
1597
|
if (message.content) {
|
|
1598
1598
|
const preview = message.content.substring(0, 500);
|
|
1599
1599
|
if (preview.length < message.content.length) {
|
|
1600
|
-
console.log(
|
|
1600
|
+
console.log(chalk7.gray(preview + "... (truncated)"));
|
|
1601
1601
|
} else {
|
|
1602
|
-
console.log(
|
|
1602
|
+
console.log(chalk7.gray(preview));
|
|
1603
1603
|
}
|
|
1604
1604
|
}
|
|
1605
1605
|
logger.newline();
|
|
@@ -1613,7 +1613,7 @@ var StreamingRenderer = class {
|
|
|
1613
1613
|
if (message.usage) {
|
|
1614
1614
|
const { tokens_in, tokens_out, cost_usd } = message.usage;
|
|
1615
1615
|
console.log(
|
|
1616
|
-
|
|
1616
|
+
chalk7.gray(
|
|
1617
1617
|
`\u{1F4CA} Tokens: ${tokens_in.toLocaleString()} in, ${tokens_out.toLocaleString()} out | Cost: $${cost_usd.toFixed(4)}`
|
|
1618
1618
|
)
|
|
1619
1619
|
);
|
|
@@ -1791,7 +1791,7 @@ async function memorySave(projectId, content) {
|
|
|
1791
1791
|
logger.error("Please provide content to save");
|
|
1792
1792
|
return;
|
|
1793
1793
|
}
|
|
1794
|
-
const { type } = await
|
|
1794
|
+
const { type } = await inquirer5.prompt([
|
|
1795
1795
|
{
|
|
1796
1796
|
type: "list",
|
|
1797
1797
|
name: "type",
|
|
@@ -1832,7 +1832,7 @@ async function memoryShow(projectId) {
|
|
|
1832
1832
|
for (const memory of memories) {
|
|
1833
1833
|
const preview = memory.content.length > 100 ? memory.content.substring(0, 100) + "..." : memory.content;
|
|
1834
1834
|
table.push([
|
|
1835
|
-
|
|
1835
|
+
chalk7.cyan(memory.type),
|
|
1836
1836
|
preview,
|
|
1837
1837
|
new Date(memory.created_at).toLocaleDateString()
|
|
1838
1838
|
]);
|
|
@@ -1844,7 +1844,7 @@ async function memoryShow(projectId) {
|
|
|
1844
1844
|
}
|
|
1845
1845
|
}
|
|
1846
1846
|
async function memoryClear(projectId) {
|
|
1847
|
-
const { confirm } = await
|
|
1847
|
+
const { confirm } = await inquirer5.prompt([
|
|
1848
1848
|
{
|
|
1849
1849
|
type: "confirm",
|
|
1850
1850
|
name: "confirm",
|
|
@@ -1873,7 +1873,7 @@ async function configCommand(action, key, value) {
|
|
|
1873
1873
|
if (!action || action === "list") {
|
|
1874
1874
|
const config = configManager.getAll();
|
|
1875
1875
|
const apiKey = await configManager.getApiKey();
|
|
1876
|
-
console.log(
|
|
1876
|
+
console.log(chalk7.bold("Current Configuration:"));
|
|
1877
1877
|
console.log(` API URL: ${config.apiUrl}`);
|
|
1878
1878
|
console.log(` API Key: ${apiKey ? apiKey.substring(0, 12) + "..." : "Not set"}`);
|
|
1879
1879
|
console.log(` Workspace: ${config.workspaceId || "Not set"}`);
|
|
@@ -1881,7 +1881,7 @@ async function configCommand(action, key, value) {
|
|
|
1881
1881
|
console.log(` Model: ${config.model || "Auto"}`);
|
|
1882
1882
|
console.log(` Mode: ${config.mode || "Auto"}`);
|
|
1883
1883
|
console.log();
|
|
1884
|
-
console.log(
|
|
1884
|
+
console.log(chalk7.gray(`Config file: ${configManager.getPath()}`));
|
|
1885
1885
|
return;
|
|
1886
1886
|
}
|
|
1887
1887
|
if (action === "get") {
|
|
@@ -1972,26 +1972,26 @@ async function handleSpecialCommand(command, session, options) {
|
|
|
1972
1972
|
const mainCmd = parts[0].toLowerCase();
|
|
1973
1973
|
if (cmd === "/help") {
|
|
1974
1974
|
logger.box([
|
|
1975
|
-
|
|
1975
|
+
chalk7.bold("DevWing Chat Commands:"),
|
|
1976
1976
|
"",
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1977
|
+
chalk7.bold.yellow("Navigation & Session:"),
|
|
1978
|
+
chalk7.cyan("/help") + " - Show this help message",
|
|
1979
|
+
chalk7.cyan("/exit, /quit") + " - Exit chat mode",
|
|
1980
|
+
chalk7.cyan("/clear") + " - Clear conversation history",
|
|
1981
|
+
chalk7.cyan("/context") + " - Show current context",
|
|
1982
|
+
chalk7.cyan("/history") + " - Show conversation history",
|
|
1983
1983
|
"",
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1984
|
+
chalk7.bold.yellow("Specialized Commands:"),
|
|
1985
|
+
chalk7.cyan("/scan") + " - Run security vulnerability scan",
|
|
1986
|
+
chalk7.cyan("/review") + " - Perform code review",
|
|
1987
|
+
chalk7.cyan("/explain <target>") + " - Explain code, file, or concept",
|
|
1988
1988
|
"",
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1989
|
+
chalk7.bold.yellow("Project Memory:"),
|
|
1990
|
+
chalk7.cyan("/memory save <content>") + " - Save note to project memory",
|
|
1991
|
+
chalk7.cyan("/memory show") + " - Show all project memories",
|
|
1992
|
+
chalk7.cyan("/memory clear") + " - Clear all project memories",
|
|
1993
1993
|
"",
|
|
1994
|
-
|
|
1994
|
+
chalk7.dim("Just type your message to chat with DevWing!")
|
|
1995
1995
|
].join("\n"), { title: "Chat Commands", color: "blue" });
|
|
1996
1996
|
return true;
|
|
1997
1997
|
}
|
|
@@ -2013,9 +2013,9 @@ async function handleSpecialCommand(command, session, options) {
|
|
|
2013
2013
|
return true;
|
|
2014
2014
|
}
|
|
2015
2015
|
logger.box([
|
|
2016
|
-
|
|
2016
|
+
chalk7.bold("Current Context:"),
|
|
2017
2017
|
"",
|
|
2018
|
-
`Working Directory: ${
|
|
2018
|
+
`Working Directory: ${chalk7.cyan(context.cwd)}`,
|
|
2019
2019
|
`Shell: ${context.shell}`,
|
|
2020
2020
|
`OS: ${context.os}`,
|
|
2021
2021
|
`Files: ${context.files.length}`,
|
|
@@ -2034,7 +2034,7 @@ async function handleSpecialCommand(command, session, options) {
|
|
|
2034
2034
|
return true;
|
|
2035
2035
|
}
|
|
2036
2036
|
logger.box([
|
|
2037
|
-
|
|
2037
|
+
chalk7.bold("Conversation History:"),
|
|
2038
2038
|
"",
|
|
2039
2039
|
session.getConversationSummary()
|
|
2040
2040
|
].join("\n"), { title: "History", color: "magenta" });
|
|
@@ -2090,11 +2090,11 @@ async function handleSpecialCommand(command, session, options) {
|
|
|
2090
2090
|
}
|
|
2091
2091
|
function printWelcomeBanner() {
|
|
2092
2092
|
logger.box([
|
|
2093
|
-
|
|
2093
|
+
chalk7.bold.cyan("DevWing AI - Interactive Chat Mode"),
|
|
2094
2094
|
"",
|
|
2095
2095
|
"Chat with AI about your codebase. Type your message and press Enter.",
|
|
2096
2096
|
"",
|
|
2097
|
-
|
|
2097
|
+
chalk7.dim("Type /help for commands \u2022 /exit to quit")
|
|
2098
2098
|
].join("\n"), { title: "\u{1F680} Welcome", color: "cyan" });
|
|
2099
2099
|
}
|
|
2100
2100
|
async function chatCommand(options) {
|
|
@@ -2111,7 +2111,7 @@ async function chatCommand(options) {
|
|
|
2111
2111
|
const rl = readline.createInterface({
|
|
2112
2112
|
input: process.stdin,
|
|
2113
2113
|
output: process.stdout,
|
|
2114
|
-
prompt:
|
|
2114
|
+
prompt: chalk7.bold.green("You: ")
|
|
2115
2115
|
});
|
|
2116
2116
|
rl.on("SIGINT", () => {
|
|
2117
2117
|
logger.newline();
|
|
@@ -2144,7 +2144,7 @@ async function chatCommand(options) {
|
|
|
2144
2144
|
}
|
|
2145
2145
|
session.addMessage("user", userMessage);
|
|
2146
2146
|
logger.newline();
|
|
2147
|
-
logger.info(
|
|
2147
|
+
logger.info(chalk7.dim("DevWing is thinking..."));
|
|
2148
2148
|
logger.newline();
|
|
2149
2149
|
try {
|
|
2150
2150
|
const request = {
|
|
@@ -2310,14 +2310,14 @@ function installUpdate(packageName, packageManager, version) {
|
|
|
2310
2310
|
}
|
|
2311
2311
|
function displayUpdateInfo(currentVersion, latestVersion) {
|
|
2312
2312
|
console.log();
|
|
2313
|
-
console.log(
|
|
2314
|
-
console.log(
|
|
2315
|
-
console.log(
|
|
2313
|
+
console.log(chalk7.cyan("\u2501".repeat(60)));
|
|
2314
|
+
console.log(chalk7.bold.cyan(" DevWing CLI Update Available"));
|
|
2315
|
+
console.log(chalk7.cyan("\u2501".repeat(60)));
|
|
2316
2316
|
console.log();
|
|
2317
|
-
console.log(` ${
|
|
2318
|
-
console.log(` ${
|
|
2317
|
+
console.log(` ${chalk7.gray("Current version:")} ${chalk7.yellow(currentVersion)}`);
|
|
2318
|
+
console.log(` ${chalk7.gray("Latest version:")} ${chalk7.green(latestVersion)}`);
|
|
2319
2319
|
console.log();
|
|
2320
|
-
console.log(
|
|
2320
|
+
console.log(chalk7.gray(" Changelog: https://github.com/devwing/devwing/releases"));
|
|
2321
2321
|
console.log();
|
|
2322
2322
|
}
|
|
2323
2323
|
async function updateCommand(options) {
|
|
@@ -2325,7 +2325,7 @@ async function updateCommand(options) {
|
|
|
2325
2325
|
const currentVersion = getCurrentVersion();
|
|
2326
2326
|
const packageName = getPackageName();
|
|
2327
2327
|
console.log();
|
|
2328
|
-
console.log(
|
|
2328
|
+
console.log(chalk7.cyan(`\u{1F4E6} DevWing CLI v${currentVersion}`));
|
|
2329
2329
|
console.log();
|
|
2330
2330
|
logger.startSpinner("Checking for updates...");
|
|
2331
2331
|
const latestVersion = await getLatestVersion(packageName);
|
|
@@ -2349,9 +2349,9 @@ async function updateCommand(options) {
|
|
|
2349
2349
|
logger.error("No package manager found. Please install npm, pnpm, or yarn.");
|
|
2350
2350
|
return;
|
|
2351
2351
|
}
|
|
2352
|
-
logger.info(`Detected package manager: ${
|
|
2352
|
+
logger.info(`Detected package manager: ${chalk7.cyan(packageManager)}`);
|
|
2353
2353
|
if (!options?.force) {
|
|
2354
|
-
const { confirm } = await
|
|
2354
|
+
const { confirm } = await inquirer5.prompt([
|
|
2355
2355
|
{
|
|
2356
2356
|
type: "confirm",
|
|
2357
2357
|
name: "confirm",
|
|
@@ -2370,13 +2370,13 @@ async function updateCommand(options) {
|
|
|
2370
2370
|
console.log();
|
|
2371
2371
|
logger.success(`Successfully updated to v${latestVersion}`);
|
|
2372
2372
|
console.log();
|
|
2373
|
-
console.log(
|
|
2373
|
+
console.log(chalk7.gray(' Run "devwing --version" to verify'));
|
|
2374
2374
|
console.log();
|
|
2375
2375
|
} else {
|
|
2376
2376
|
console.log();
|
|
2377
2377
|
logger.error("Update failed. Please try manually:");
|
|
2378
2378
|
console.log();
|
|
2379
|
-
console.log(
|
|
2379
|
+
console.log(chalk7.gray(` ${packageManager} install -g ${packageName}@${latestVersion}`));
|
|
2380
2380
|
console.log();
|
|
2381
2381
|
process.exit(1);
|
|
2382
2382
|
}
|
|
@@ -2385,6 +2385,770 @@ async function updateCommand(options) {
|
|
|
2385
2385
|
process.exit(1);
|
|
2386
2386
|
}
|
|
2387
2387
|
}
|
|
2388
|
+
var TYPING_SPEED = 16;
|
|
2389
|
+
var PAUSE_SHORT = 300;
|
|
2390
|
+
var PAUSE_MEDIUM = 800;
|
|
2391
|
+
var PAUSE_LONG = 1500;
|
|
2392
|
+
function sleep(ms) {
|
|
2393
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
2394
|
+
}
|
|
2395
|
+
async function typeOut(text, speed = TYPING_SPEED) {
|
|
2396
|
+
for (const char of text) {
|
|
2397
|
+
process.stdout.write(char);
|
|
2398
|
+
await sleep(speed);
|
|
2399
|
+
}
|
|
2400
|
+
}
|
|
2401
|
+
async function typeLine(text, speed = TYPING_SPEED) {
|
|
2402
|
+
await typeOut(text, speed);
|
|
2403
|
+
process.stdout.write("\n");
|
|
2404
|
+
}
|
|
2405
|
+
var DEMO_USER = {
|
|
2406
|
+
email: "aliyu@devwing.ai",
|
|
2407
|
+
full_name: "Aliyu Mohammed",
|
|
2408
|
+
subscription_plan: "pro"};
|
|
2409
|
+
var DEMO_API_KEY = "dw_sk_demo_4xK9mQzR2pLvBnWcYjEtHuFi";
|
|
2410
|
+
var DEMO_PASSWORD = "DevWing2026!";
|
|
2411
|
+
var DEMO_WORKSPACE = {
|
|
2412
|
+
name: "Kano State Government",
|
|
2413
|
+
plan: "enterprise"};
|
|
2414
|
+
var DEMO_PROJECT = {
|
|
2415
|
+
name: "Kano Smart API"};
|
|
2416
|
+
var DEMO_MEMORIES = [
|
|
2417
|
+
{ id: "mem_001", type: "rule", content: "All API responses must include Hausa language support", created_at: "2026-03-01T10:00:00Z" },
|
|
2418
|
+
{ id: "mem_002", type: "context", content: "Auth uses JWT with RS256 signing, 1hr expiry, refresh tokens in Redis", created_at: "2026-03-05T14:00:00Z" },
|
|
2419
|
+
{ id: "mem_003", type: "decision", content: "Chose PostgreSQL over MongoDB for ACID compliance \u2014 government data requires strict consistency", created_at: "2026-03-10T09:00:00Z" },
|
|
2420
|
+
{ id: "mem_004", type: "summary", content: 'Kano Smart serves 16M residents via kanostate.gov.ng and smart.kano.gov.ng \u2014 all responses branded "Powered by devwing.ai"', created_at: "2026-03-15T11:00:00Z" }
|
|
2421
|
+
];
|
|
2422
|
+
async function demoLoginCommand() {
|
|
2423
|
+
try {
|
|
2424
|
+
const existingKey = await configManager.getApiKey();
|
|
2425
|
+
if (existingKey) {
|
|
2426
|
+
const { overwrite } = await inquirer5.prompt([
|
|
2427
|
+
{
|
|
2428
|
+
type: "confirm",
|
|
2429
|
+
name: "overwrite",
|
|
2430
|
+
message: "You are already logged in. Do you want to log in with a different account?",
|
|
2431
|
+
default: false
|
|
2432
|
+
}
|
|
2433
|
+
]);
|
|
2434
|
+
if (!overwrite) {
|
|
2435
|
+
logger.info("Login cancelled");
|
|
2436
|
+
return;
|
|
2437
|
+
}
|
|
2438
|
+
}
|
|
2439
|
+
logger.printBanner();
|
|
2440
|
+
logger.newline();
|
|
2441
|
+
const { method } = await inquirer5.prompt([
|
|
2442
|
+
{
|
|
2443
|
+
type: "list",
|
|
2444
|
+
name: "method",
|
|
2445
|
+
message: "How would you like to authenticate?",
|
|
2446
|
+
choices: [
|
|
2447
|
+
{ name: "Via Browser (easiest)", value: "browser" },
|
|
2448
|
+
{ name: "Email & Password", value: "email" },
|
|
2449
|
+
{ name: "API Key (from dashboard)", value: "apikey" }
|
|
2450
|
+
]
|
|
2451
|
+
}
|
|
2452
|
+
]);
|
|
2453
|
+
if (method === "email") {
|
|
2454
|
+
await demoLoginEmail();
|
|
2455
|
+
} else if (method === "browser") {
|
|
2456
|
+
await demoLoginBrowser();
|
|
2457
|
+
} else {
|
|
2458
|
+
await demoLoginApiKey();
|
|
2459
|
+
}
|
|
2460
|
+
} catch (error) {
|
|
2461
|
+
logger.error("Login failed");
|
|
2462
|
+
if (error.message) logger.error(error.message);
|
|
2463
|
+
process.exit(1);
|
|
2464
|
+
}
|
|
2465
|
+
}
|
|
2466
|
+
async function demoLoginEmail() {
|
|
2467
|
+
const { email, password } = await inquirer5.prompt([
|
|
2468
|
+
{
|
|
2469
|
+
type: "input",
|
|
2470
|
+
name: "email",
|
|
2471
|
+
message: "Email:",
|
|
2472
|
+
validate: (input) => {
|
|
2473
|
+
if (!input.includes("@") || !input.includes(".")) return "Please enter a valid email address";
|
|
2474
|
+
return true;
|
|
2475
|
+
}
|
|
2476
|
+
},
|
|
2477
|
+
{
|
|
2478
|
+
type: "password",
|
|
2479
|
+
name: "password",
|
|
2480
|
+
message: "Password:",
|
|
2481
|
+
mask: "*"
|
|
2482
|
+
}
|
|
2483
|
+
]);
|
|
2484
|
+
logger.startSpinner("Authenticating...");
|
|
2485
|
+
await sleep(1200);
|
|
2486
|
+
if (email !== DEMO_USER.email || password !== DEMO_PASSWORD) {
|
|
2487
|
+
logger.failSpinner("Authentication failed");
|
|
2488
|
+
logger.error("Incorrect email or password");
|
|
2489
|
+
return;
|
|
2490
|
+
}
|
|
2491
|
+
logger.updateSpinner("Creating API key...");
|
|
2492
|
+
await sleep(800);
|
|
2493
|
+
await configManager.setApiKey(DEMO_API_KEY);
|
|
2494
|
+
logger.succeedSpinner(`Welcome back, ${DEMO_USER.full_name}!`);
|
|
2495
|
+
logger.success(`Plan: ${DEMO_USER.subscription_plan.toUpperCase()}`);
|
|
2496
|
+
logger.newline();
|
|
2497
|
+
showQuickStart2();
|
|
2498
|
+
}
|
|
2499
|
+
async function demoLoginBrowser() {
|
|
2500
|
+
logger.startSpinner("Initiating browser authentication...");
|
|
2501
|
+
await sleep(1e3);
|
|
2502
|
+
logger.succeedSpinner("Device code created");
|
|
2503
|
+
logger.newline();
|
|
2504
|
+
logger.success("Browser opened! Please log in to authorize this device.");
|
|
2505
|
+
logger.newline();
|
|
2506
|
+
logger.info(chalk7.dim("Device code: fSibdCwR..."));
|
|
2507
|
+
logger.startSpinner("Waiting for authorization...");
|
|
2508
|
+
await sleep(3e3);
|
|
2509
|
+
logger.updateSpinner("Authorization received!");
|
|
2510
|
+
await sleep(500);
|
|
2511
|
+
await configManager.setApiKey(DEMO_API_KEY);
|
|
2512
|
+
logger.updateSpinner("Fetching profile...");
|
|
2513
|
+
await sleep(600);
|
|
2514
|
+
logger.succeedSpinner(`Welcome, ${DEMO_USER.full_name}!`);
|
|
2515
|
+
logger.success(`Plan: ${DEMO_USER.subscription_plan.toUpperCase()}`);
|
|
2516
|
+
logger.newline();
|
|
2517
|
+
showQuickStart2();
|
|
2518
|
+
}
|
|
2519
|
+
async function demoLoginApiKey() {
|
|
2520
|
+
logger.info(`Get your API key from the dashboard`);
|
|
2521
|
+
logger.newline();
|
|
2522
|
+
const { apiKey } = await inquirer5.prompt([
|
|
2523
|
+
{
|
|
2524
|
+
type: "password",
|
|
2525
|
+
name: "apiKey",
|
|
2526
|
+
message: "API Key:",
|
|
2527
|
+
mask: "*",
|
|
2528
|
+
validate: (input) => {
|
|
2529
|
+
if (!input.startsWith("dw_sk_")) return 'Invalid API key format. Should start with "dw_sk_"';
|
|
2530
|
+
if (input.length < 20) return "API key seems too short";
|
|
2531
|
+
return true;
|
|
2532
|
+
}
|
|
2533
|
+
}
|
|
2534
|
+
]);
|
|
2535
|
+
logger.startSpinner("Verifying API key...");
|
|
2536
|
+
await sleep(1e3);
|
|
2537
|
+
if (apiKey !== DEMO_API_KEY) {
|
|
2538
|
+
logger.failSpinner("Invalid API key");
|
|
2539
|
+
logger.error("API key not recognized");
|
|
2540
|
+
return;
|
|
2541
|
+
}
|
|
2542
|
+
await configManager.setApiKey(apiKey);
|
|
2543
|
+
logger.succeedSpinner(`Authenticated as ${DEMO_USER.full_name}!`);
|
|
2544
|
+
logger.success(`Plan: ${DEMO_USER.subscription_plan.toUpperCase()}`);
|
|
2545
|
+
logger.newline();
|
|
2546
|
+
showQuickStart2();
|
|
2547
|
+
}
|
|
2548
|
+
function showQuickStart2() {
|
|
2549
|
+
logger.box(
|
|
2550
|
+
[
|
|
2551
|
+
"Quick Start:",
|
|
2552
|
+
"",
|
|
2553
|
+
' devwing "fix the auth bug" Ask anything',
|
|
2554
|
+
" devwing chat Interactive chat mode",
|
|
2555
|
+
" devwing scan Security scan",
|
|
2556
|
+
" devwing review Code review",
|
|
2557
|
+
" devwing explain file.js Explain code",
|
|
2558
|
+
"",
|
|
2559
|
+
"For more commands, run: devwing --help"
|
|
2560
|
+
].join("\n"),
|
|
2561
|
+
{ title: "Ready to go!", color: "green" }
|
|
2562
|
+
);
|
|
2563
|
+
}
|
|
2564
|
+
async function demoStatusCommand() {
|
|
2565
|
+
const apiKey = await configManager.getApiKey();
|
|
2566
|
+
if (!apiKey) {
|
|
2567
|
+
logger.warn("Not authenticated");
|
|
2568
|
+
logger.info('Run "devwing login" to get started');
|
|
2569
|
+
return;
|
|
2570
|
+
}
|
|
2571
|
+
logger.startSpinner("Fetching profile...");
|
|
2572
|
+
await sleep(800);
|
|
2573
|
+
logger.succeedSpinner("Authenticated");
|
|
2574
|
+
logger.newline();
|
|
2575
|
+
console.log(chalk7.bold("User Profile:"));
|
|
2576
|
+
console.log(` Email: ${DEMO_USER.email}`);
|
|
2577
|
+
console.log(` Name: ${DEMO_USER.full_name}`);
|
|
2578
|
+
console.log(` Plan: ${DEMO_USER.subscription_plan.toUpperCase()}`);
|
|
2579
|
+
console.log(` Verified: ${chalk7.green("Yes")}`);
|
|
2580
|
+
console.log(` 2FA: ${chalk7.dim("Disabled")}`);
|
|
2581
|
+
console.log(` Workspace: ${DEMO_WORKSPACE.name} (${DEMO_WORKSPACE.plan})`);
|
|
2582
|
+
console.log(` Project: ${DEMO_PROJECT.name}`);
|
|
2583
|
+
logger.newline();
|
|
2584
|
+
}
|
|
2585
|
+
async function demoLogoutCommand() {
|
|
2586
|
+
const apiKey = await configManager.getApiKey();
|
|
2587
|
+
if (!apiKey) {
|
|
2588
|
+
logger.info("You are not logged in");
|
|
2589
|
+
return;
|
|
2590
|
+
}
|
|
2591
|
+
const { confirm } = await inquirer5.prompt([
|
|
2592
|
+
{
|
|
2593
|
+
type: "confirm",
|
|
2594
|
+
name: "confirm",
|
|
2595
|
+
message: "Are you sure you want to log out?",
|
|
2596
|
+
default: false
|
|
2597
|
+
}
|
|
2598
|
+
]);
|
|
2599
|
+
if (!confirm) {
|
|
2600
|
+
logger.info("Logout cancelled");
|
|
2601
|
+
return;
|
|
2602
|
+
}
|
|
2603
|
+
await configManager.deleteApiKey();
|
|
2604
|
+
const apiUrl = configManager.getApiUrl();
|
|
2605
|
+
configManager.clear();
|
|
2606
|
+
configManager.setApiUrl(apiUrl);
|
|
2607
|
+
logger.success("Successfully logged out");
|
|
2608
|
+
}
|
|
2609
|
+
async function demoMemoryCommand(action, content) {
|
|
2610
|
+
if (action === "show") {
|
|
2611
|
+
logger.startSpinner("Loading project memory...");
|
|
2612
|
+
await sleep(800);
|
|
2613
|
+
logger.stopSpinner();
|
|
2614
|
+
logger.success(`Found ${DEMO_MEMORIES.length} memory items`);
|
|
2615
|
+
logger.newline();
|
|
2616
|
+
const table = new Table({
|
|
2617
|
+
head: [chalk7.bold("Type"), chalk7.bold("Content"), chalk7.bold("Created")],
|
|
2618
|
+
colWidths: [15, 60, 15],
|
|
2619
|
+
wordWrap: true
|
|
2620
|
+
});
|
|
2621
|
+
for (const memory of DEMO_MEMORIES) {
|
|
2622
|
+
const preview = memory.content.length > 80 ? memory.content.substring(0, 80) + "..." : memory.content;
|
|
2623
|
+
table.push([
|
|
2624
|
+
chalk7.cyan(memory.type),
|
|
2625
|
+
preview,
|
|
2626
|
+
new Date(memory.created_at).toLocaleDateString()
|
|
2627
|
+
]);
|
|
2628
|
+
}
|
|
2629
|
+
console.log(table.toString());
|
|
2630
|
+
} else if (action === "save") {
|
|
2631
|
+
if (!content) {
|
|
2632
|
+
logger.error("Please provide content to save");
|
|
2633
|
+
return;
|
|
2634
|
+
}
|
|
2635
|
+
const { type } = await inquirer5.prompt([
|
|
2636
|
+
{
|
|
2637
|
+
type: "list",
|
|
2638
|
+
name: "type",
|
|
2639
|
+
message: "What type of memory is this?",
|
|
2640
|
+
choices: [
|
|
2641
|
+
{ name: "Context (general information)", value: "context" },
|
|
2642
|
+
{ name: "Rule (coding standard or guideline)", value: "rule" },
|
|
2643
|
+
{ name: "Decision (architectural decision)", value: "decision" },
|
|
2644
|
+
{ name: "Summary (project summary)", value: "summary" }
|
|
2645
|
+
]
|
|
2646
|
+
}
|
|
2647
|
+
]);
|
|
2648
|
+
logger.startSpinner("Saving to project memory...");
|
|
2649
|
+
await sleep(1e3);
|
|
2650
|
+
logger.succeedSpinner(`Memory saved as ${type}`);
|
|
2651
|
+
} else if (action === "clear") {
|
|
2652
|
+
const { confirm } = await inquirer5.prompt([
|
|
2653
|
+
{
|
|
2654
|
+
type: "confirm",
|
|
2655
|
+
name: "confirm",
|
|
2656
|
+
message: "Are you sure you want to clear all project memory?",
|
|
2657
|
+
default: false
|
|
2658
|
+
}
|
|
2659
|
+
]);
|
|
2660
|
+
if (!confirm) {
|
|
2661
|
+
logger.info("Operation cancelled");
|
|
2662
|
+
return;
|
|
2663
|
+
}
|
|
2664
|
+
logger.startSpinner("Clearing project memory...");
|
|
2665
|
+
await sleep(800);
|
|
2666
|
+
logger.succeedSpinner("Project memory cleared");
|
|
2667
|
+
}
|
|
2668
|
+
}
|
|
2669
|
+
async function demoExplainCommand(target) {
|
|
2670
|
+
await requireDemoAuth();
|
|
2671
|
+
await showContextLoading(24, "general");
|
|
2672
|
+
logger.startSpinner("DevWing is thinking...");
|
|
2673
|
+
await sleep(1500);
|
|
2674
|
+
logger.stopSpinner();
|
|
2675
|
+
console.log();
|
|
2676
|
+
await typeLine(chalk7.bold.magenta(`## Explaining: ${target}`));
|
|
2677
|
+
console.log();
|
|
2678
|
+
if (target.includes("auth") || target.includes("middleware")) {
|
|
2679
|
+
await typeLine(`The auth middleware at ${chalk7.cyan(target)} handles request authentication:`);
|
|
2680
|
+
console.log();
|
|
2681
|
+
await typeLine(" 1. Extracts the JWT token from the `Authorization: Bearer` header");
|
|
2682
|
+
await typeLine(" 2. Verifies the token signature using RS256 algorithm");
|
|
2683
|
+
await typeLine(" 3. Checks token expiration and issuer claims");
|
|
2684
|
+
await typeLine(" 4. Loads the user from the database using the `sub` claim");
|
|
2685
|
+
await typeLine(" 5. Attaches the user object to `req.user` for downstream handlers");
|
|
2686
|
+
console.log();
|
|
2687
|
+
await typeLine(chalk7.bold("Key patterns:"));
|
|
2688
|
+
await typeLine(` ${chalk7.green("\u2022")} Uses decorator pattern \u2014 ${chalk7.cyan("@authenticate")} on route handlers`);
|
|
2689
|
+
await typeLine(` ${chalk7.green("\u2022")} Implements token refresh via ${chalk7.cyan("X-Refresh-Token")} header`);
|
|
2690
|
+
await typeLine(` ${chalk7.green("\u2022")} Rate limited to prevent brute force (10 req/min per IP)`);
|
|
2691
|
+
console.log();
|
|
2692
|
+
await typeLine(chalk7.dim("This middleware is critical \u2014 it protects all /api/* routes."));
|
|
2693
|
+
} else {
|
|
2694
|
+
await typeLine(`${chalk7.cyan(target)} is a core component of the system.`);
|
|
2695
|
+
console.log();
|
|
2696
|
+
await typeLine(` ${chalk7.green("\u2022")} Responsible for handling ${target}-related logic`);
|
|
2697
|
+
await typeLine(` ${chalk7.green("\u2022")} Integrates with the main application pipeline`);
|
|
2698
|
+
await typeLine(` ${chalk7.green("\u2022")} Well-structured with clear separation of concerns`);
|
|
2699
|
+
}
|
|
2700
|
+
await showUsage(1200, 680, 45e-4, "general");
|
|
2701
|
+
logger.success("Done!");
|
|
2702
|
+
}
|
|
2703
|
+
async function demoReviewCommand() {
|
|
2704
|
+
await requireDemoAuth();
|
|
2705
|
+
logger.info("Performing code review...");
|
|
2706
|
+
logger.newline();
|
|
2707
|
+
await showContextLoading(38, "general");
|
|
2708
|
+
logger.startSpinner("DevWing is thinking...");
|
|
2709
|
+
await sleep(2e3);
|
|
2710
|
+
logger.stopSpinner();
|
|
2711
|
+
console.log();
|
|
2712
|
+
await typeLine(chalk7.bold.magenta("## Code Review \u2014 Recent Changes"));
|
|
2713
|
+
console.log();
|
|
2714
|
+
await typeLine(chalk7.bold("Files reviewed: 5 | Commits: 3 | Lines changed: +142 / -67"));
|
|
2715
|
+
console.log();
|
|
2716
|
+
await typeLine(chalk7.green.bold(" \u2713 GOOD") + " \u2014 Error handling is consistent across all new endpoints");
|
|
2717
|
+
await typeLine(chalk7.green.bold(" \u2713 GOOD") + " \u2014 Database queries use parameterized statements");
|
|
2718
|
+
await typeLine(chalk7.green.bold(" \u2713 GOOD") + " \u2014 TypeScript strict mode is properly enforced");
|
|
2719
|
+
console.log();
|
|
2720
|
+
await typeLine(chalk7.yellow.bold(" \u25B2 SUGGESTION") + " \u2014 Consider extracting the validation logic at " + chalk7.cyan("user_service.ts:45"));
|
|
2721
|
+
await typeLine(chalk7.gray(" The email regex pattern is duplicated in 3 files. Move to a shared validator."));
|
|
2722
|
+
console.log();
|
|
2723
|
+
await typeLine(chalk7.yellow.bold(" \u25B2 SUGGESTION") + " \u2014 Missing error boundary in " + chalk7.cyan("api_handler.ts:78"));
|
|
2724
|
+
await typeLine(chalk7.gray(" The async handler doesn't catch unhandled promise rejections. Wrap in try/catch."));
|
|
2725
|
+
console.log();
|
|
2726
|
+
await typeLine(chalk7.red.bold(" \u25A0 ISSUE") + " \u2014 Potential memory leak in " + chalk7.cyan("websocket_manager.ts:120"));
|
|
2727
|
+
await typeLine(chalk7.gray(" Event listeners are added on each connection but never removed on disconnect."));
|
|
2728
|
+
console.log();
|
|
2729
|
+
await typeLine(chalk7.bold("Summary: ") + chalk7.green("3 good") + " | " + chalk7.yellow("2 suggestions") + " | " + chalk7.red("1 issue"));
|
|
2730
|
+
await showUsage(2800, 1200, 95e-4, "general");
|
|
2731
|
+
logger.success("Done!");
|
|
2732
|
+
}
|
|
2733
|
+
async function demoPromptCommand(prompt, options) {
|
|
2734
|
+
await requireDemoAuth();
|
|
2735
|
+
const promptLower = prompt.toLowerCase();
|
|
2736
|
+
if (promptLower.includes("refactor") && promptLower.includes("auth")) {
|
|
2737
|
+
await demoBackendRefactor();
|
|
2738
|
+
} else if (promptLower.includes("scan") || promptLower.includes("owasp") || promptLower.includes("vulnerabilit")) {
|
|
2739
|
+
await demoSecurityScan();
|
|
2740
|
+
} else if (promptLower.includes("explain")) {
|
|
2741
|
+
const target = prompt.replace(/explain\s*/i, "").trim() || "this code";
|
|
2742
|
+
await demoExplainCommand(target);
|
|
2743
|
+
} else if (promptLower.includes("review")) {
|
|
2744
|
+
await demoReviewCommand();
|
|
2745
|
+
} else if (promptLower.includes("deploy") || promptLower.includes("docker") || promptLower.includes("kubernetes")) {
|
|
2746
|
+
await demoDeployAssist();
|
|
2747
|
+
} else if (promptLower.includes("fix") || promptLower.includes("bug") || promptLower.includes("error")) {
|
|
2748
|
+
await demoBugFix();
|
|
2749
|
+
} else if (promptLower.includes("test") || promptLower.includes("spec")) {
|
|
2750
|
+
await demoWriteTests();
|
|
2751
|
+
} else {
|
|
2752
|
+
await demoGenericPrompt(prompt, options);
|
|
2753
|
+
}
|
|
2754
|
+
}
|
|
2755
|
+
async function demoChatCommand(options) {
|
|
2756
|
+
await requireDemoAuth();
|
|
2757
|
+
const readline2 = await import('readline');
|
|
2758
|
+
console.clear();
|
|
2759
|
+
logger.box([
|
|
2760
|
+
chalk7.bold.cyan("DevWing AI - Interactive Chat Mode"),
|
|
2761
|
+
"",
|
|
2762
|
+
"Chat with AI about your codebase. Type your message and press Enter.",
|
|
2763
|
+
"",
|
|
2764
|
+
chalk7.dim("Type /help for commands | /exit to quit")
|
|
2765
|
+
].join("\n"), { title: "Welcome", color: "cyan" });
|
|
2766
|
+
logger.newline();
|
|
2767
|
+
logger.startSpinner("Loading codebase context...");
|
|
2768
|
+
await sleep(1200);
|
|
2769
|
+
logger.succeedSpinner("Context loaded \u2014 34 files");
|
|
2770
|
+
logger.newline();
|
|
2771
|
+
const rl = readline2.createInterface({
|
|
2772
|
+
input: process.stdin,
|
|
2773
|
+
output: process.stdout,
|
|
2774
|
+
prompt: chalk7.bold.green("You: ")
|
|
2775
|
+
});
|
|
2776
|
+
rl.on("SIGINT", () => {
|
|
2777
|
+
logger.newline();
|
|
2778
|
+
logger.info("Exiting chat mode...");
|
|
2779
|
+
rl.close();
|
|
2780
|
+
process.exit(0);
|
|
2781
|
+
});
|
|
2782
|
+
rl.prompt();
|
|
2783
|
+
rl.on("line", async (input) => {
|
|
2784
|
+
const msg = input.trim();
|
|
2785
|
+
if (!msg) {
|
|
2786
|
+
rl.prompt();
|
|
2787
|
+
return;
|
|
2788
|
+
}
|
|
2789
|
+
if (msg === "/exit" || msg === "/quit") {
|
|
2790
|
+
logger.info("Exiting chat mode...");
|
|
2791
|
+
rl.close();
|
|
2792
|
+
return;
|
|
2793
|
+
}
|
|
2794
|
+
if (msg === "/help") {
|
|
2795
|
+
logger.box([
|
|
2796
|
+
chalk7.bold("DevWing Chat Commands:"),
|
|
2797
|
+
"",
|
|
2798
|
+
chalk7.cyan("/help") + " \u2014 Show this message",
|
|
2799
|
+
chalk7.cyan("/exit") + " \u2014 Exit chat mode",
|
|
2800
|
+
chalk7.cyan("/scan") + " \u2014 Security scan",
|
|
2801
|
+
chalk7.cyan("/review") + " \u2014 Code review",
|
|
2802
|
+
chalk7.cyan("/context") + " \u2014 Show loaded context",
|
|
2803
|
+
chalk7.cyan("/memory show") + " \u2014 Show project memory"
|
|
2804
|
+
].join("\n"), { title: "Commands", color: "blue" });
|
|
2805
|
+
logger.newline();
|
|
2806
|
+
rl.prompt();
|
|
2807
|
+
return;
|
|
2808
|
+
}
|
|
2809
|
+
if (msg === "/context") {
|
|
2810
|
+
logger.box([
|
|
2811
|
+
chalk7.bold("Current Context:"),
|
|
2812
|
+
"",
|
|
2813
|
+
`Working Directory: ${chalk7.cyan(process.cwd())}`,
|
|
2814
|
+
`Shell: zsh | OS: darwin`,
|
|
2815
|
+
`Files: 34 | Git Branch: main`,
|
|
2816
|
+
"",
|
|
2817
|
+
"Top files:",
|
|
2818
|
+
" \u2022 src/auth/auth_controller.ts",
|
|
2819
|
+
" \u2022 src/middleware/jwt.middleware.ts",
|
|
2820
|
+
" \u2022 src/services/user_service.ts",
|
|
2821
|
+
" \u2022 package.json",
|
|
2822
|
+
" ... and 30 more"
|
|
2823
|
+
].join("\n"), { title: "Context", color: "cyan" });
|
|
2824
|
+
logger.newline();
|
|
2825
|
+
rl.prompt();
|
|
2826
|
+
return;
|
|
2827
|
+
}
|
|
2828
|
+
if (msg === "/memory show") {
|
|
2829
|
+
await demoMemoryCommand("show");
|
|
2830
|
+
logger.newline();
|
|
2831
|
+
rl.prompt();
|
|
2832
|
+
return;
|
|
2833
|
+
}
|
|
2834
|
+
if (msg === "/scan") {
|
|
2835
|
+
logger.newline();
|
|
2836
|
+
await demoSecurityScan();
|
|
2837
|
+
logger.newline();
|
|
2838
|
+
rl.prompt();
|
|
2839
|
+
return;
|
|
2840
|
+
}
|
|
2841
|
+
if (msg === "/review") {
|
|
2842
|
+
logger.newline();
|
|
2843
|
+
await demoReviewCommand();
|
|
2844
|
+
logger.newline();
|
|
2845
|
+
rl.prompt();
|
|
2846
|
+
return;
|
|
2847
|
+
}
|
|
2848
|
+
logger.newline();
|
|
2849
|
+
logger.info(chalk7.dim("DevWing is thinking..."));
|
|
2850
|
+
logger.newline();
|
|
2851
|
+
await demoPromptCommand(msg, options);
|
|
2852
|
+
logger.newline();
|
|
2853
|
+
rl.prompt();
|
|
2854
|
+
});
|
|
2855
|
+
rl.on("close", () => {
|
|
2856
|
+
logger.newline();
|
|
2857
|
+
logger.success("Chat session ended. Goodbye!");
|
|
2858
|
+
process.exit(0);
|
|
2859
|
+
});
|
|
2860
|
+
}
|
|
2861
|
+
async function demoBackendRefactor() {
|
|
2862
|
+
await showContextLoading(47, "backend");
|
|
2863
|
+
logger.startSpinner("DevWing is thinking...");
|
|
2864
|
+
await sleep(2e3);
|
|
2865
|
+
logger.stopSpinner();
|
|
2866
|
+
console.log();
|
|
2867
|
+
await typeLine(chalk7.bold.magenta("## Analysis"));
|
|
2868
|
+
console.log();
|
|
2869
|
+
await typeLine("I've analyzed the auth service across 6 files. Here's what I found:");
|
|
2870
|
+
console.log();
|
|
2871
|
+
await typeLine(` ${chalk7.red("\u2022")} Auth logic, validation, and DB queries are tightly coupled in ${chalk7.cyan("auth_controller.ts")}`);
|
|
2872
|
+
await typeLine(` ${chalk7.red("\u2022")} JWT validation is duplicated across 4 route handlers`);
|
|
2873
|
+
await typeLine(` ${chalk7.red("\u2022")} No rate limiting on login/register endpoints`);
|
|
2874
|
+
await typeLine(` ${chalk7.red("\u2022")} User lookup queries repeated in 3 places`);
|
|
2875
|
+
console.log();
|
|
2876
|
+
await sleep(PAUSE_MEDIUM);
|
|
2877
|
+
await typeLine(chalk7.bold.magenta("## Refactor Plan"));
|
|
2878
|
+
console.log();
|
|
2879
|
+
await typeLine("1. Extract JWT validation into reusable middleware");
|
|
2880
|
+
await typeLine("2. Create `UserRepository` with all DB operations");
|
|
2881
|
+
await typeLine("3. Add `RateLimiter` decorator using sliding window algorithm");
|
|
2882
|
+
await typeLine("4. Separate controller into thin route handlers");
|
|
2883
|
+
console.log();
|
|
2884
|
+
await sleep(PAUSE_MEDIUM);
|
|
2885
|
+
await typeLine(chalk7.bold.magenta("## Executing Refactor"));
|
|
2886
|
+
console.log();
|
|
2887
|
+
await showToolCall("read_file", "src/auth/auth_controller.ts");
|
|
2888
|
+
await showToolResult("Read 156 lines");
|
|
2889
|
+
await showToolCall("read_file", "src/auth/auth_routes.ts");
|
|
2890
|
+
await showToolResult("Read 89 lines");
|
|
2891
|
+
await showToolCall("read_file", "src/middleware/index.ts");
|
|
2892
|
+
await showToolResult("Read 23 lines");
|
|
2893
|
+
await showToolCall("write_file", "src/auth/repositories/user.repository.ts");
|
|
2894
|
+
await sleep(PAUSE_LONG);
|
|
2895
|
+
await showToolResult("Created \u2014 UserRepository with findByEmail, findById, create, updateLastLogin");
|
|
2896
|
+
await showToolCall("write_file", "src/middleware/jwt.middleware.ts");
|
|
2897
|
+
await sleep(PAUSE_MEDIUM);
|
|
2898
|
+
await showToolResult("Created \u2014 JWT validation middleware with token extraction and verification");
|
|
2899
|
+
await showToolCall("write_file", "src/middleware/rate-limiter.ts");
|
|
2900
|
+
await sleep(PAUSE_MEDIUM);
|
|
2901
|
+
await showToolResult("Created \u2014 Sliding window rate limiter: 10 req/min login, 5 req/min register");
|
|
2902
|
+
await showToolCall("write_file", "src/auth/auth_controller.ts");
|
|
2903
|
+
await sleep(PAUSE_LONG);
|
|
2904
|
+
await showToolResult("Refactored \u2014 156 lines \u2192 89 lines, clean separation of concerns");
|
|
2905
|
+
await showToolCall("run_command", "npm test -- --filter auth");
|
|
2906
|
+
await sleep(2e3);
|
|
2907
|
+
await showToolResult("All 12 tests passed");
|
|
2908
|
+
console.log();
|
|
2909
|
+
await sleep(PAUSE_MEDIUM);
|
|
2910
|
+
await typeLine(chalk7.bold.magenta("## Summary"));
|
|
2911
|
+
console.log();
|
|
2912
|
+
await typeLine(chalk7.green("\u2713") + " Extracted JWT validation to dedicated middleware");
|
|
2913
|
+
await typeLine(chalk7.green("\u2713") + " Created UserRepository \u2014 single source for all user DB operations");
|
|
2914
|
+
await typeLine(chalk7.green("\u2713") + " Added rate limiting: 10 req/min (login), 5 req/min (register)");
|
|
2915
|
+
await typeLine(chalk7.green("\u2713") + ` Reduced ${chalk7.red("156")} lines \u2192 ${chalk7.green("89")} lines (${chalk7.cyan("43% reduction")})`);
|
|
2916
|
+
await typeLine(chalk7.green("\u2713") + " All 12 existing tests pass \u2014 no regressions");
|
|
2917
|
+
console.log();
|
|
2918
|
+
await typeLine(chalk7.dim("Files modified: 4 | Files created: 3 | Lines removed: 67"));
|
|
2919
|
+
await showUsage(3420, 1890, 0.0127, "backend");
|
|
2920
|
+
logger.success("Done!");
|
|
2921
|
+
}
|
|
2922
|
+
async function demoSecurityScan() {
|
|
2923
|
+
await showContextLoading(32, "security");
|
|
2924
|
+
logger.startSpinner("DevWing is thinking...");
|
|
2925
|
+
await sleep(1800);
|
|
2926
|
+
logger.stopSpinner();
|
|
2927
|
+
console.log();
|
|
2928
|
+
await typeLine(chalk7.bold.magenta("## OWASP Security Scan \u2014 Auth Service"));
|
|
2929
|
+
console.log();
|
|
2930
|
+
await showToolCall("read_file", "src/auth/auth_controller.ts");
|
|
2931
|
+
await showToolResult("Read 89 lines");
|
|
2932
|
+
await showToolCall("read_file", "src/middleware/jwt.middleware.ts");
|
|
2933
|
+
await showToolResult("Read 34 lines");
|
|
2934
|
+
await showToolCall("read_file", "src/auth/repositories/user.repository.ts");
|
|
2935
|
+
await showToolResult("Read 52 lines");
|
|
2936
|
+
await sleep(PAUSE_LONG);
|
|
2937
|
+
await typeLine(chalk7.bold.red("## Vulnerabilities Found: 3"));
|
|
2938
|
+
console.log();
|
|
2939
|
+
await typeLine(chalk7.red.bold(" \u25A0 CRITICAL") + chalk7.red(" \u2014 Weak JWT Token Configuration"));
|
|
2940
|
+
await typeLine(chalk7.gray(" Location: src/middleware/jwt.middleware.ts:12"));
|
|
2941
|
+
await typeLine(chalk7.gray(" Issue: JWT tokens signed with HS256 and no expiration validation"));
|
|
2942
|
+
await typeLine(chalk7.gray(" OWASP: A2:2021 \u2014 Cryptographic Failures"));
|
|
2943
|
+
console.log();
|
|
2944
|
+
await typeLine(chalk7.yellow.bold(" \u25A0 HIGH") + chalk7.yellow(" \u2014 SQL Injection via User Lookup"));
|
|
2945
|
+
await typeLine(chalk7.gray(" Location: src/auth/repositories/user.repository.ts:18"));
|
|
2946
|
+
await typeLine(chalk7.gray(" Issue: Raw string interpolation in findByEmail query"));
|
|
2947
|
+
await typeLine(chalk7.gray(" OWASP: A3:2021 \u2014 Injection"));
|
|
2948
|
+
console.log();
|
|
2949
|
+
await typeLine(chalk7.yellow.bold(" \u25A0 MEDIUM") + chalk7.yellow(" \u2014 Missing Password Complexity Enforcement"));
|
|
2950
|
+
await typeLine(chalk7.gray(" Location: src/auth/auth_controller.ts:34"));
|
|
2951
|
+
await typeLine(chalk7.gray(" Issue: No minimum length, complexity, or breach database check"));
|
|
2952
|
+
await typeLine(chalk7.gray(" OWASP: A7:2021 \u2014 Identification and Authentication Failures"));
|
|
2953
|
+
console.log();
|
|
2954
|
+
await sleep(PAUSE_MEDIUM);
|
|
2955
|
+
await typeLine(chalk7.bold.magenta("## Applying Security Patches"));
|
|
2956
|
+
console.log();
|
|
2957
|
+
await showToolCall("write_file", "src/middleware/jwt.middleware.ts");
|
|
2958
|
+
await sleep(PAUSE_LONG);
|
|
2959
|
+
await showToolResult("Patched \u2014 Added RS256 signing, exp/iat validation, token rotation support");
|
|
2960
|
+
await showToolCall("write_file", "src/auth/repositories/user.repository.ts");
|
|
2961
|
+
await sleep(PAUSE_MEDIUM);
|
|
2962
|
+
await showToolResult("Patched \u2014 Parameterized queries, input sanitization added");
|
|
2963
|
+
await showToolCall("write_file", "src/auth/validators/password.validator.ts");
|
|
2964
|
+
await sleep(PAUSE_MEDIUM);
|
|
2965
|
+
await showToolResult("Created \u2014 Min 12 chars, uppercase, number, special char, breach check");
|
|
2966
|
+
await showToolCall("run_command", "npm test -- --filter auth");
|
|
2967
|
+
await sleep(1500);
|
|
2968
|
+
await showToolResult("All 15 tests passed (3 new security tests added)");
|
|
2969
|
+
console.log();
|
|
2970
|
+
await typeLine(chalk7.bold.magenta("## Scan Complete"));
|
|
2971
|
+
console.log();
|
|
2972
|
+
await typeLine(chalk7.green("\u2713") + " 3 vulnerabilities found and patched");
|
|
2973
|
+
await typeLine(chalk7.green("\u2713") + " JWT upgraded to RS256 with expiration enforcement");
|
|
2974
|
+
await typeLine(chalk7.green("\u2713") + " SQL injection eliminated via parameterized queries");
|
|
2975
|
+
await typeLine(chalk7.green("\u2713") + " Password policy enforced \u2014 NIST 800-63B compliant");
|
|
2976
|
+
await typeLine(chalk7.green("\u2713") + " 3 new security regression tests added");
|
|
2977
|
+
console.log();
|
|
2978
|
+
await typeLine(chalk7.dim("Severity breakdown: 1 critical, 1 high, 1 medium \u2014 all resolved"));
|
|
2979
|
+
await showUsage(2180, 1450, 89e-4, "security");
|
|
2980
|
+
logger.success("Done!");
|
|
2981
|
+
}
|
|
2982
|
+
async function demoDeployAssist(_prompt) {
|
|
2983
|
+
await showContextLoading(18, "devops");
|
|
2984
|
+
logger.startSpinner("DevWing is thinking...");
|
|
2985
|
+
await sleep(1800);
|
|
2986
|
+
logger.stopSpinner();
|
|
2987
|
+
console.log();
|
|
2988
|
+
await typeLine(chalk7.bold.magenta("## Deployment Analysis"));
|
|
2989
|
+
console.log();
|
|
2990
|
+
await showToolCall("read_file", "Dockerfile");
|
|
2991
|
+
await showToolResult("Read 28 lines");
|
|
2992
|
+
await showToolCall("read_file", "docker-compose.yml");
|
|
2993
|
+
await showToolResult("Read 45 lines");
|
|
2994
|
+
await showToolCall("read_file", ".github/workflows/deploy.yml");
|
|
2995
|
+
await showToolResult("Read 67 lines");
|
|
2996
|
+
await sleep(PAUSE_MEDIUM);
|
|
2997
|
+
await typeLine("Current deployment configuration:");
|
|
2998
|
+
console.log();
|
|
2999
|
+
await typeLine(` ${chalk7.green("\u2022")} Docker image: ${chalk7.cyan("node:20-alpine")} \u2014 multi-stage build`);
|
|
3000
|
+
await typeLine(` ${chalk7.green("\u2022")} Target: Railway (detected from ${chalk7.cyan("railway.toml")})`);
|
|
3001
|
+
await typeLine(` ${chalk7.green("\u2022")} Health check: ${chalk7.cyan("/health")} endpoint configured`);
|
|
3002
|
+
await typeLine(` ${chalk7.green("\u2022")} Environment: 12 vars configured, 3 secrets`);
|
|
3003
|
+
console.log();
|
|
3004
|
+
await typeLine(chalk7.bold.magenta("## Optimizations Applied"));
|
|
3005
|
+
console.log();
|
|
3006
|
+
await showToolCall("write_file", "Dockerfile");
|
|
3007
|
+
await sleep(PAUSE_LONG);
|
|
3008
|
+
await showToolResult("Optimized \u2014 Added layer caching, reduced image size from 340MB to 89MB");
|
|
3009
|
+
await showToolCall("write_file", "docker-compose.yml");
|
|
3010
|
+
await sleep(PAUSE_MEDIUM);
|
|
3011
|
+
await showToolResult("Added health checks, restart policies, resource limits");
|
|
3012
|
+
console.log();
|
|
3013
|
+
await typeLine(chalk7.green("\u2713") + " Docker image optimized: 340MB \u2192 89MB (74% smaller)");
|
|
3014
|
+
await typeLine(chalk7.green("\u2713") + " Added proper health checks and restart policies");
|
|
3015
|
+
await typeLine(chalk7.green("\u2713") + " Build time reduced: ~4min \u2192 ~1.5min with layer caching");
|
|
3016
|
+
await showUsage(1800, 950, 65e-4, "devops");
|
|
3017
|
+
logger.success("Done!");
|
|
3018
|
+
}
|
|
3019
|
+
async function demoBugFix(_prompt) {
|
|
3020
|
+
await showContextLoading(35, "backend");
|
|
3021
|
+
logger.startSpinner("DevWing is thinking...");
|
|
3022
|
+
await sleep(1800);
|
|
3023
|
+
logger.stopSpinner();
|
|
3024
|
+
console.log();
|
|
3025
|
+
await typeLine(chalk7.bold.magenta("## Bug Analysis"));
|
|
3026
|
+
console.log();
|
|
3027
|
+
await showToolCall("read_file", "src/services/user_service.ts");
|
|
3028
|
+
await showToolResult("Read 94 lines");
|
|
3029
|
+
await showToolCall("git_diff", "");
|
|
3030
|
+
await showToolResult("3 files changed, 12 insertions, 4 deletions");
|
|
3031
|
+
await showToolCall("git_log", "last 5 commits");
|
|
3032
|
+
await showToolResult("Loaded recent history");
|
|
3033
|
+
await sleep(PAUSE_MEDIUM);
|
|
3034
|
+
await typeLine("I found the issue. The bug is in " + chalk7.cyan("user_service.ts:67") + ":");
|
|
3035
|
+
console.log();
|
|
3036
|
+
await typeLine(chalk7.red(" - const user = await db.query(`SELECT * FROM users WHERE id = ${userId}`);"));
|
|
3037
|
+
await typeLine(chalk7.green(" + const user = await db.query(`SELECT * FROM users WHERE id = $1`, [userId]);"));
|
|
3038
|
+
console.log();
|
|
3039
|
+
await typeLine("The user ID was being interpolated directly into the SQL query instead of");
|
|
3040
|
+
await typeLine("using parameterized binding. This causes:");
|
|
3041
|
+
await typeLine(" 1. SQL injection vulnerability");
|
|
3042
|
+
await typeLine(" 2. Query failure when ID contains special characters");
|
|
3043
|
+
console.log();
|
|
3044
|
+
await showToolCall("write_file", "src/services/user_service.ts");
|
|
3045
|
+
await sleep(PAUSE_LONG);
|
|
3046
|
+
await showToolResult("Fixed \u2014 Parameterized query binding");
|
|
3047
|
+
await showToolCall("run_command", "npm test -- --filter user");
|
|
3048
|
+
await sleep(1500);
|
|
3049
|
+
await showToolResult("All 8 tests passed");
|
|
3050
|
+
console.log();
|
|
3051
|
+
await typeLine(chalk7.green("\u2713") + " Bug fixed \u2014 parameterized SQL query");
|
|
3052
|
+
await typeLine(chalk7.green("\u2713") + " SQL injection vulnerability closed");
|
|
3053
|
+
await typeLine(chalk7.green("\u2713") + " All tests pass");
|
|
3054
|
+
await showUsage(1650, 890, 62e-4, "backend");
|
|
3055
|
+
logger.success("Done!");
|
|
3056
|
+
}
|
|
3057
|
+
async function demoWriteTests(_prompt) {
|
|
3058
|
+
await showContextLoading(22, "backend");
|
|
3059
|
+
logger.startSpinner("DevWing is thinking...");
|
|
3060
|
+
await sleep(1800);
|
|
3061
|
+
logger.stopSpinner();
|
|
3062
|
+
console.log();
|
|
3063
|
+
await typeLine(chalk7.bold.magenta("## Generating Tests"));
|
|
3064
|
+
console.log();
|
|
3065
|
+
await showToolCall("read_file", "src/auth/auth_controller.ts");
|
|
3066
|
+
await showToolResult("Read 89 lines \u2014 4 functions to test");
|
|
3067
|
+
await showToolCall("read_file", "src/auth/repositories/user.repository.ts");
|
|
3068
|
+
await showToolResult("Read 52 lines \u2014 5 methods to test");
|
|
3069
|
+
await sleep(PAUSE_MEDIUM);
|
|
3070
|
+
await showToolCall("write_file", "tests/auth/auth_controller.test.ts");
|
|
3071
|
+
await sleep(PAUSE_LONG);
|
|
3072
|
+
await showToolResult("Created \u2014 8 test cases: login, register, refresh, logout, edge cases");
|
|
3073
|
+
await showToolCall("write_file", "tests/auth/user_repository.test.ts");
|
|
3074
|
+
await sleep(PAUSE_LONG);
|
|
3075
|
+
await showToolResult("Created \u2014 10 test cases: CRUD operations, error handling, constraints");
|
|
3076
|
+
await showToolCall("run_command", "npm test -- --filter auth");
|
|
3077
|
+
await sleep(2e3);
|
|
3078
|
+
await showToolResult("18 / 18 tests passed");
|
|
3079
|
+
console.log();
|
|
3080
|
+
await typeLine(chalk7.bold.magenta("## Test Coverage"));
|
|
3081
|
+
console.log();
|
|
3082
|
+
await typeLine(" auth_controller.ts: " + chalk7.green("94%") + " (was 0%)");
|
|
3083
|
+
await typeLine(" user.repository.ts: " + chalk7.green("98%") + " (was 0%)");
|
|
3084
|
+
await typeLine(" Overall auth module: " + chalk7.green("96%"));
|
|
3085
|
+
console.log();
|
|
3086
|
+
await typeLine(chalk7.green("\u2713") + " 18 tests generated and passing");
|
|
3087
|
+
await typeLine(chalk7.green("\u2713") + " Covers happy paths, edge cases, and error states");
|
|
3088
|
+
await showUsage(2400, 1680, 98e-4, "backend");
|
|
3089
|
+
logger.success("Done!");
|
|
3090
|
+
}
|
|
3091
|
+
async function demoGenericPrompt(prompt, options) {
|
|
3092
|
+
const mode = options.mode || "general";
|
|
3093
|
+
await showContextLoading(28, mode);
|
|
3094
|
+
logger.startSpinner("DevWing is thinking...");
|
|
3095
|
+
await sleep(1500);
|
|
3096
|
+
logger.stopSpinner();
|
|
3097
|
+
console.log();
|
|
3098
|
+
await typeLine(chalk7.bold.magenta("## Response"));
|
|
3099
|
+
console.log();
|
|
3100
|
+
await typeLine(`Based on your codebase analysis, here's my approach to: "${prompt}"`);
|
|
3101
|
+
console.log();
|
|
3102
|
+
await typeLine(` ${chalk7.green("\u2022")} Analyzed project structure and dependencies`);
|
|
3103
|
+
await typeLine(` ${chalk7.green("\u2022")} Reviewed relevant files based on your prompt`);
|
|
3104
|
+
await typeLine(` ${chalk7.green("\u2022")} Generated solution aligned with existing patterns`);
|
|
3105
|
+
console.log();
|
|
3106
|
+
await showUsage(1100, 620, 41e-4, mode);
|
|
3107
|
+
logger.success("Done!");
|
|
3108
|
+
}
|
|
3109
|
+
async function showToolCall(tool, args) {
|
|
3110
|
+
await sleep(PAUSE_SHORT);
|
|
3111
|
+
console.log();
|
|
3112
|
+
console.log(chalk7.yellow(` \u25B6 ${chalk7.bold(tool)}`));
|
|
3113
|
+
if (args) console.log(chalk7.gray(` ${args}`));
|
|
3114
|
+
await sleep(PAUSE_MEDIUM);
|
|
3115
|
+
}
|
|
3116
|
+
async function showToolResult(result) {
|
|
3117
|
+
console.log(chalk7.green(` \u2713 ${result}`));
|
|
3118
|
+
await sleep(PAUSE_SHORT);
|
|
3119
|
+
}
|
|
3120
|
+
async function showContextLoading(fileCount, mode) {
|
|
3121
|
+
logger.startSpinner("Analyzing codebase...");
|
|
3122
|
+
await sleep(1200);
|
|
3123
|
+
logger.updateSpinner(`Scanning ${fileCount} files...`);
|
|
3124
|
+
await sleep(800);
|
|
3125
|
+
logger.updateSpinner("Building dependency graph...");
|
|
3126
|
+
await sleep(600);
|
|
3127
|
+
logger.updateSpinner("Collecting git context...");
|
|
3128
|
+
await sleep(500);
|
|
3129
|
+
logger.succeedSpinner(`Context loaded \u2014 ${fileCount} files, mode: ${chalk7.cyan(mode)}`);
|
|
3130
|
+
console.log();
|
|
3131
|
+
}
|
|
3132
|
+
async function showUsage(tokensIn, tokensOut, cost, mode) {
|
|
3133
|
+
const modelName = mode === "security" ? "devwing-security-1" : mode === "devops" ? "devwing-devops-1" : mode === "frontend" ? "devwing-frontend-1" : "devwing-backend-1";
|
|
3134
|
+
console.log();
|
|
3135
|
+
console.log(
|
|
3136
|
+
chalk7.gray(
|
|
3137
|
+
`\u{1F4CA} Tokens: ${tokensIn.toLocaleString()} in, ${tokensOut.toLocaleString()} out | Cost: $${cost.toFixed(4)} | Model: ${modelName}`
|
|
3138
|
+
)
|
|
3139
|
+
);
|
|
3140
|
+
console.log();
|
|
3141
|
+
}
|
|
3142
|
+
async function requireDemoAuth() {
|
|
3143
|
+
const apiKey = await configManager.getApiKey();
|
|
3144
|
+
if (!apiKey) {
|
|
3145
|
+
logger.error('Not authenticated. Run "devwing login" first.');
|
|
3146
|
+
process.exit(1);
|
|
3147
|
+
}
|
|
3148
|
+
}
|
|
3149
|
+
function isDemoMode() {
|
|
3150
|
+
return process.env.DEVWING_DEMO === "1" || process.env.DEVWING_DEMO === "true";
|
|
3151
|
+
}
|
|
2388
3152
|
var program = new Command();
|
|
2389
3153
|
var __cliFilename = fileURLToPath(import.meta.url);
|
|
2390
3154
|
var __cliDirname = dirname(__cliFilename);
|
|
@@ -2406,42 +3170,82 @@ var VERSION = getVersion();
|
|
|
2406
3170
|
program.name("devwing").description("DevWing.ai - Your AI Wingman in the Terminal").version(VERSION, "-v, --version", "Display version number").helpOption("-h, --help", "Display help information");
|
|
2407
3171
|
program.option("--mode <mode>", "AI mode: general|frontend|backend|security|devops").option("--model <model>", "Specific model to use").option("--project <id>", "Project ID").option("--workspace <id>", "Workspace ID").option("--verbose", "Verbose output for debugging").option("-y, --yes", "Auto-confirm destructive actions");
|
|
2408
3172
|
program.command("login").description("Authenticate with DevWing").action(async () => {
|
|
2409
|
-
|
|
3173
|
+
if (isDemoMode()) {
|
|
3174
|
+
await demoLoginCommand();
|
|
3175
|
+
} else {
|
|
3176
|
+
await loginCommand();
|
|
3177
|
+
}
|
|
2410
3178
|
});
|
|
2411
3179
|
program.command("logout").description("Clear stored credentials").action(async () => {
|
|
2412
|
-
|
|
3180
|
+
if (isDemoMode()) {
|
|
3181
|
+
await demoLogoutCommand();
|
|
3182
|
+
} else {
|
|
3183
|
+
await logoutCommand();
|
|
3184
|
+
}
|
|
2413
3185
|
});
|
|
2414
3186
|
program.command("status").description("Show authentication status and profile").action(async () => {
|
|
2415
|
-
|
|
3187
|
+
if (isDemoMode()) {
|
|
3188
|
+
await demoStatusCommand();
|
|
3189
|
+
} else {
|
|
3190
|
+
await statusCommand();
|
|
3191
|
+
}
|
|
2416
3192
|
});
|
|
2417
3193
|
program.command("chat").description("Start interactive chat mode (like Claude Code)").action(async () => {
|
|
2418
3194
|
const opts = program.opts();
|
|
2419
|
-
|
|
3195
|
+
if (isDemoMode()) {
|
|
3196
|
+
await demoChatCommand(opts);
|
|
3197
|
+
} else {
|
|
3198
|
+
await chatCommand(opts);
|
|
3199
|
+
}
|
|
2420
3200
|
});
|
|
2421
3201
|
program.command("scan").description("Run security vulnerability scan").action(async () => {
|
|
2422
3202
|
const opts = program.opts();
|
|
2423
|
-
|
|
3203
|
+
if (isDemoMode()) {
|
|
3204
|
+
await demoPromptCommand("scan auth service for OWASP vulnerabilities", { ...opts, mode: "security" });
|
|
3205
|
+
} else {
|
|
3206
|
+
await scanCommand(opts);
|
|
3207
|
+
}
|
|
2424
3208
|
});
|
|
2425
3209
|
program.command("review").description("Perform code review on recent changes").action(async () => {
|
|
2426
3210
|
const opts = program.opts();
|
|
2427
|
-
|
|
3211
|
+
if (isDemoMode()) {
|
|
3212
|
+
await demoReviewCommand();
|
|
3213
|
+
} else {
|
|
3214
|
+
await reviewCommand(opts);
|
|
3215
|
+
}
|
|
2428
3216
|
});
|
|
2429
3217
|
program.command("explain <target>").description("Explain code, file, or concept").action(async (target) => {
|
|
2430
3218
|
const opts = program.opts();
|
|
2431
|
-
|
|
3219
|
+
if (isDemoMode()) {
|
|
3220
|
+
await demoExplainCommand(target);
|
|
3221
|
+
} else {
|
|
3222
|
+
await explainCommand(target, opts);
|
|
3223
|
+
}
|
|
2432
3224
|
});
|
|
2433
3225
|
var memoryCmd = program.command("memory").description("Manage project memory");
|
|
2434
3226
|
memoryCmd.command("save <content>").description("Save information to project memory").action(async (content) => {
|
|
2435
3227
|
const opts = program.opts();
|
|
2436
|
-
|
|
3228
|
+
if (isDemoMode()) {
|
|
3229
|
+
await demoMemoryCommand("save", content);
|
|
3230
|
+
} else {
|
|
3231
|
+
await memoryCommand("save", content, opts);
|
|
3232
|
+
}
|
|
2437
3233
|
});
|
|
2438
3234
|
memoryCmd.command("show").description("Show all project memories").action(async () => {
|
|
2439
3235
|
const opts = program.opts();
|
|
2440
|
-
|
|
3236
|
+
if (isDemoMode()) {
|
|
3237
|
+
await demoMemoryCommand("show");
|
|
3238
|
+
} else {
|
|
3239
|
+
await memoryCommand("show", void 0, opts);
|
|
3240
|
+
}
|
|
2441
3241
|
});
|
|
2442
3242
|
memoryCmd.command("clear").description("Clear all project memories").action(async () => {
|
|
2443
3243
|
const opts = program.opts();
|
|
2444
|
-
|
|
3244
|
+
if (isDemoMode()) {
|
|
3245
|
+
await demoMemoryCommand("clear");
|
|
3246
|
+
} else {
|
|
3247
|
+
await memoryCommand("clear", void 0, opts);
|
|
3248
|
+
}
|
|
2445
3249
|
});
|
|
2446
3250
|
var configCmd = program.command("config").description("Manage CLI configuration");
|
|
2447
3251
|
configCmd.command("list").description("Show all configuration").action(async () => {
|
|
@@ -2459,11 +3263,19 @@ program.command("update").description("Check for and install CLI updates").optio
|
|
|
2459
3263
|
program.argument("[prompt...]", "Natural language prompt for AI (optional - starts chat mode if empty)").action(async (promptParts) => {
|
|
2460
3264
|
const opts = program.opts();
|
|
2461
3265
|
if (promptParts.length === 0) {
|
|
2462
|
-
|
|
3266
|
+
if (isDemoMode()) {
|
|
3267
|
+
await demoChatCommand(opts);
|
|
3268
|
+
} else {
|
|
3269
|
+
await chatCommand(opts);
|
|
3270
|
+
}
|
|
2463
3271
|
return;
|
|
2464
3272
|
}
|
|
2465
3273
|
const prompt = promptParts.join(" ");
|
|
2466
|
-
|
|
3274
|
+
if (isDemoMode()) {
|
|
3275
|
+
await demoPromptCommand(prompt, opts);
|
|
3276
|
+
} else {
|
|
3277
|
+
await promptCommand(prompt, opts);
|
|
3278
|
+
}
|
|
2467
3279
|
});
|
|
2468
3280
|
program.exitOverride();
|
|
2469
3281
|
try {
|