lovecode-ai 0.1.6 → 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/dist/index.js +692 -281
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -82,7 +82,7 @@ import {
|
|
|
82
82
|
|
|
83
83
|
// src/index.ts
|
|
84
84
|
import { Command as Command17 } from "commander";
|
|
85
|
-
import
|
|
85
|
+
import chalk44 from "chalk";
|
|
86
86
|
|
|
87
87
|
// src/commands/chat.ts
|
|
88
88
|
import { Command } from "commander";
|
|
@@ -4990,8 +4990,121 @@ configCommand.action(cmdShow);
|
|
|
4990
4990
|
|
|
4991
4991
|
// src/commands/env.ts
|
|
4992
4992
|
import { Command as Command5 } from "commander";
|
|
4993
|
-
import
|
|
4993
|
+
import chalk25 from "chalk";
|
|
4994
4994
|
import inquirer from "inquirer";
|
|
4995
|
+
|
|
4996
|
+
// src/platform/detect.ts
|
|
4997
|
+
import * as fs17 from "fs";
|
|
4998
|
+
import * as os from "os";
|
|
4999
|
+
var _isTermux = null;
|
|
5000
|
+
var _isCodespaces = null;
|
|
5001
|
+
var _isTouch = null;
|
|
5002
|
+
function isTermux() {
|
|
5003
|
+
if (_isTermux !== null) return _isTermux;
|
|
5004
|
+
_isTermux = fs17.existsSync("/data/data/com.termux") || process.env.PREFIX === "/data/data/com.termux/files/usr" || !!process.env.TERMUX_VERSION || process.env.TERMUX_APK_RELEASE !== void 0 || false;
|
|
5005
|
+
return _isTermux;
|
|
5006
|
+
}
|
|
5007
|
+
function isTouchDevice() {
|
|
5008
|
+
if (_isTouch !== null) return _isTouch;
|
|
5009
|
+
_isTouch = isTermux() || process.env.LOVECODE_TOUCH === "true" || process.env.TERMUX_VERSION !== void 0 || false;
|
|
5010
|
+
return _isTouch;
|
|
5011
|
+
}
|
|
5012
|
+
function isCodespaces() {
|
|
5013
|
+
if (_isCodespaces !== null) return _isCodespaces;
|
|
5014
|
+
_isCodespaces = process.env.CODESPACES === "true" || !!process.env.CODESPACE_NAME || fs17.existsSync("/.codespaces") || false;
|
|
5015
|
+
return _isCodespaces;
|
|
5016
|
+
}
|
|
5017
|
+
function lowRamMode() {
|
|
5018
|
+
const totalMem = os.totalmem();
|
|
5019
|
+
const memMb = totalMem / (1024 * 1024);
|
|
5020
|
+
return memMb < 1024 || process.env.LOVECODE_LOW_RAM === "true" || isTermux();
|
|
5021
|
+
}
|
|
5022
|
+
function recommendedMaxMemory() {
|
|
5023
|
+
const totalMem = os.totalmem();
|
|
5024
|
+
const memMb = totalMem / (1024 * 1024);
|
|
5025
|
+
if (isTermux()) return 128;
|
|
5026
|
+
if (memMb < 1024) return 128;
|
|
5027
|
+
if (memMb < 2048) return 256;
|
|
5028
|
+
if (memMb < 4096) return 512;
|
|
5029
|
+
return 1024;
|
|
5030
|
+
}
|
|
5031
|
+
function termuxInfo() {
|
|
5032
|
+
if (!isTermux()) return [];
|
|
5033
|
+
const info = [];
|
|
5034
|
+
info.push(`Termux version: ${process.env.TERMUX_VERSION || "unknown"}`);
|
|
5035
|
+
info.push(`Touch optimized: ${isTouchDevice() ? "yes" : "no"}`);
|
|
5036
|
+
info.push(`Low RAM mode: ${lowRamMode() ? "enabled" : "disabled"}`);
|
|
5037
|
+
info.push(`Max memory: ${recommendedMaxMemory()}MB`);
|
|
5038
|
+
info.push(`Cache TTL: ${cacheTTL()}ms`);
|
|
5039
|
+
return info;
|
|
5040
|
+
}
|
|
5041
|
+
function cacheTTL() {
|
|
5042
|
+
return isTermux() ? 12e4 : 3e5;
|
|
5043
|
+
}
|
|
5044
|
+
function platformInfo() {
|
|
5045
|
+
const info = [];
|
|
5046
|
+
info.push(`OS: ${os.platform()} ${os.release()}`);
|
|
5047
|
+
info.push(`CPU: ${os.cpus().length} cores`);
|
|
5048
|
+
info.push(`RAM: ${(os.totalmem() / 1024 ** 3).toFixed(1)} GB total, ${(os.freemem() / 1024 ** 3).toFixed(1)} GB free`);
|
|
5049
|
+
info.push(`Node: ${process.version}`);
|
|
5050
|
+
if (isTermux()) info.push(`Platform: Termux (Android) ${process.env.TERMUX_VERSION || ""}`);
|
|
5051
|
+
if (isCodespaces()) info.push("Platform: GitHub Codespaces");
|
|
5052
|
+
return info;
|
|
5053
|
+
}
|
|
5054
|
+
|
|
5055
|
+
// src/utils/select.ts
|
|
5056
|
+
import * as readline5 from "readline";
|
|
5057
|
+
import chalk24 from "chalk";
|
|
5058
|
+
async function numberedSelect(choices, options) {
|
|
5059
|
+
const termux = options?.termux ?? isTermux();
|
|
5060
|
+
const pageSize = options?.pageSize || 15;
|
|
5061
|
+
const message = options?.message || "Select an option:";
|
|
5062
|
+
console.log(`
|
|
5063
|
+
${chalk24.bold(message)}
|
|
5064
|
+
`);
|
|
5065
|
+
const display = choices.slice(0, pageSize);
|
|
5066
|
+
for (let i = 0; i < display.length; i++) {
|
|
5067
|
+
const num = chalk24.cyan(`${i + 1}`.padStart(3));
|
|
5068
|
+
const desc = display[i].description ? chalk24.dim(` \u2014 ${display[i].description}`) : "";
|
|
5069
|
+
console.log(` ${num}. ${display[i].name}${desc}`);
|
|
5070
|
+
}
|
|
5071
|
+
if (choices.length > pageSize) {
|
|
5072
|
+
console.log(` ${chalk24.dim(`... and ${choices.length - pageSize} more. Use \`lovecode env set\` directly.`)}`);
|
|
5073
|
+
}
|
|
5074
|
+
const rl = readline5.createInterface({ input: process.stdin, output: process.stdout });
|
|
5075
|
+
return new Promise((resolve5) => {
|
|
5076
|
+
const prompt = termux ? `
|
|
5077
|
+
${chalk24.cyan("?")} Enter number (1-${display.length}): ` : `
|
|
5078
|
+
${chalk24.cyan("?")} Enter number (1-${display.length}), or press Enter for 1: `;
|
|
5079
|
+
rl.question(prompt, (answer) => {
|
|
5080
|
+
rl.close();
|
|
5081
|
+
const trimmed = answer.trim();
|
|
5082
|
+
if (!trimmed && !termux) {
|
|
5083
|
+
resolve5(display[0].value);
|
|
5084
|
+
return;
|
|
5085
|
+
}
|
|
5086
|
+
const num = parseInt(trimmed, 10);
|
|
5087
|
+
if (isNaN(num) || num < 1 || num > display.length) {
|
|
5088
|
+
console.log(chalk24.red(` Invalid selection. Please enter a number between 1 and ${display.length}.`));
|
|
5089
|
+
resolve5(numberedSelect(choices, options));
|
|
5090
|
+
return;
|
|
5091
|
+
}
|
|
5092
|
+
resolve5(display[num - 1].value);
|
|
5093
|
+
});
|
|
5094
|
+
});
|
|
5095
|
+
}
|
|
5096
|
+
async function promptInput(question, defaultValue) {
|
|
5097
|
+
const rl = readline5.createInterface({ input: process.stdin, output: process.stdout });
|
|
5098
|
+
const prompt = defaultValue ? ` ${chalk24.cyan("?")} ${question} ${chalk24.dim(`(${defaultValue})`)}: ` : ` ${chalk24.cyan("?")} ${question}: `;
|
|
5099
|
+
return new Promise((resolve5) => {
|
|
5100
|
+
rl.question(prompt, (answer) => {
|
|
5101
|
+
rl.close();
|
|
5102
|
+
resolve5(answer.trim() || defaultValue || "");
|
|
5103
|
+
});
|
|
5104
|
+
});
|
|
5105
|
+
}
|
|
5106
|
+
|
|
5107
|
+
// src/commands/env.ts
|
|
4995
5108
|
async function cmdEnvShow(options) {
|
|
4996
5109
|
console.log(formatEnvStatus(options.dir));
|
|
4997
5110
|
}
|
|
@@ -4999,46 +5112,65 @@ async function cmdEnvSet(key, value, options) {
|
|
|
4999
5112
|
const vars = loadEnv(options.dir);
|
|
5000
5113
|
vars[key.toUpperCase()] = value;
|
|
5001
5114
|
saveEnv(vars, options.dir);
|
|
5002
|
-
console.log(
|
|
5115
|
+
console.log(chalk25.green(`Set ${key.toUpperCase()}`));
|
|
5003
5116
|
console.log(formatEnvStatus(options.dir));
|
|
5004
5117
|
}
|
|
5005
5118
|
async function cmdEnvUnset(key, options) {
|
|
5006
5119
|
const vars = loadEnv(options.dir);
|
|
5007
5120
|
delete vars[key.toUpperCase()];
|
|
5008
5121
|
saveEnv(vars, options.dir);
|
|
5009
|
-
console.log(
|
|
5122
|
+
console.log(chalk25.yellow(`Unset ${key.toUpperCase()}`));
|
|
5010
5123
|
}
|
|
5011
5124
|
async function cmdEnvSelect(options) {
|
|
5012
5125
|
const vars = loadEnv(options.dir);
|
|
5013
5126
|
const choices = KNOWN_ENV_VARS.map((v) => {
|
|
5014
5127
|
const current = vars[v.key] || process.env[v.key] || "";
|
|
5015
|
-
const status = current ?
|
|
5128
|
+
const status = current ? chalk25.green("\u2713 set") : chalk25.dim("\u25CB empty");
|
|
5016
5129
|
const masked = current && v.key.includes("KEY") ? current.slice(0, 8) + "*".repeat(Math.min(current.length - 8, 12)) : current || "";
|
|
5017
5130
|
return {
|
|
5018
|
-
name: `${v.key.padEnd(28)} ${status}
|
|
5131
|
+
name: `${v.key.padEnd(28)} ${status}`,
|
|
5019
5132
|
value: v.key,
|
|
5020
|
-
|
|
5133
|
+
description: masked ? `${v.description} (${masked})` : v.description
|
|
5021
5134
|
};
|
|
5022
5135
|
});
|
|
5023
|
-
const
|
|
5024
|
-
|
|
5025
|
-
|
|
5026
|
-
|
|
5027
|
-
|
|
5028
|
-
|
|
5029
|
-
|
|
5136
|
+
const useTermuxStyle = isTermux() || isTouchDevice();
|
|
5137
|
+
let key;
|
|
5138
|
+
if (useTermuxStyle) {
|
|
5139
|
+
key = await numberedSelect(choices, {
|
|
5140
|
+
message: "Select an environment variable to set:",
|
|
5141
|
+
termux: true
|
|
5142
|
+
});
|
|
5143
|
+
} else {
|
|
5144
|
+
const result = await inquirer.prompt([{
|
|
5145
|
+
type: "list",
|
|
5146
|
+
name: "key",
|
|
5147
|
+
message: "Select an environment variable to set:",
|
|
5148
|
+
choices: choices.map((c) => ({ name: c.name, value: c.value })),
|
|
5149
|
+
pageSize: 15
|
|
5150
|
+
}]);
|
|
5151
|
+
key = result.key;
|
|
5152
|
+
}
|
|
5030
5153
|
const existing = vars[key] || "";
|
|
5031
5154
|
const description = KNOWN_ENV_VARS.find((v) => v.key === key)?.description || "";
|
|
5032
|
-
|
|
5033
|
-
|
|
5034
|
-
|
|
5035
|
-
|
|
5036
|
-
|
|
5037
|
-
|
|
5038
|
-
|
|
5155
|
+
let value;
|
|
5156
|
+
if (useTermuxStyle) {
|
|
5157
|
+
value = await promptInput(`Enter value for ${key} (${description})`, existing || void 0);
|
|
5158
|
+
} else {
|
|
5159
|
+
const result = await inquirer.prompt([{
|
|
5160
|
+
type: "input",
|
|
5161
|
+
name: "value",
|
|
5162
|
+
message: `Enter value for ${chalk25.cyan(key)} (${description}):`,
|
|
5163
|
+
default: existing || void 0
|
|
5164
|
+
}]);
|
|
5165
|
+
value = result.value;
|
|
5166
|
+
}
|
|
5167
|
+
if (!value) {
|
|
5168
|
+
console.log(chalk25.yellow("\nNo value entered. Skipping."));
|
|
5169
|
+
return;
|
|
5170
|
+
}
|
|
5039
5171
|
vars[key] = value;
|
|
5040
5172
|
saveEnv(vars, options.dir);
|
|
5041
|
-
console.log(
|
|
5173
|
+
console.log(chalk25.green(`
|
|
5042
5174
|
\u2713 ${key} saved`));
|
|
5043
5175
|
console.log(formatEnvStatus(options.dir));
|
|
5044
5176
|
}
|
|
@@ -5048,6 +5180,8 @@ var envCommand = new Command5("env").alias("environment").description("Manage en
|
|
|
5048
5180
|
lovecode env select Interactive variable selection
|
|
5049
5181
|
lovecode env set GROQ_API_KEY gsk_xxx
|
|
5050
5182
|
lovecode env unset GROQ_API_KEY
|
|
5183
|
+
|
|
5184
|
+
On Termux/Android: uses numbered input instead of arrow keys
|
|
5051
5185
|
`);
|
|
5052
5186
|
envCommand.command("set").description("Set an environment variable").argument("<key>", "Variable name").argument("<value>", "Variable value").option("--dir <path>", "Project directory").action(cmdEnvSet);
|
|
5053
5187
|
envCommand.command("unset").description("Remove an environment variable").argument("<key>", "Variable name").option("--dir <path>", "Project directory").action(cmdEnvUnset);
|
|
@@ -5058,57 +5192,28 @@ envCommand.action(cmdEnvShow);
|
|
|
5058
5192
|
import { Command as Command6 } from "commander";
|
|
5059
5193
|
|
|
5060
5194
|
// src/platform/optimize.ts
|
|
5061
|
-
import
|
|
5062
|
-
|
|
5063
|
-
// src/platform/detect.ts
|
|
5064
|
-
import * as fs17 from "fs";
|
|
5065
|
-
import * as os from "os";
|
|
5066
|
-
var _isTermux = null;
|
|
5067
|
-
var _isCodespaces = null;
|
|
5068
|
-
function isTermux() {
|
|
5069
|
-
if (_isTermux !== null) return _isTermux;
|
|
5070
|
-
_isTermux = fs17.existsSync("/data/data/com.termux") || process.env.PREFIX === "/data/data/com.termux/files/usr" || !!process.env.TERMUX_VERSION;
|
|
5071
|
-
return _isTermux;
|
|
5072
|
-
}
|
|
5073
|
-
function isCodespaces() {
|
|
5074
|
-
if (_isCodespaces !== null) return _isCodespaces;
|
|
5075
|
-
_isCodespaces = process.env.CODESPACES === "true" || !!process.env.CODESPACE_NAME || fs17.existsSync("/.codespaces");
|
|
5076
|
-
return _isCodespaces;
|
|
5077
|
-
}
|
|
5078
|
-
function lowRamMode() {
|
|
5079
|
-
const totalMem = os.totalmem();
|
|
5080
|
-
const memMb = totalMem / (1024 * 1024);
|
|
5081
|
-
return memMb < 1024 || process.env.LOVECODE_LOW_RAM === "true" || isTermux();
|
|
5082
|
-
}
|
|
5083
|
-
function recommendedMaxMemory() {
|
|
5084
|
-
const totalMem = os.totalmem();
|
|
5085
|
-
const memMb = totalMem / (1024 * 1024);
|
|
5086
|
-
if (memMb < 1024) return 128;
|
|
5087
|
-
if (memMb < 2048) return 256;
|
|
5088
|
-
if (memMb < 4096) return 512;
|
|
5089
|
-
return 1024;
|
|
5090
|
-
}
|
|
5091
|
-
function platformInfo() {
|
|
5092
|
-
const info = [];
|
|
5093
|
-
info.push(`OS: ${os.platform()} ${os.release()}`);
|
|
5094
|
-
info.push(`CPU: ${os.cpus().length} cores`);
|
|
5095
|
-
info.push(`RAM: ${(os.totalmem() / 1024 ** 3).toFixed(1)} GB total, ${(os.freemem() / 1024 ** 3).toFixed(1)} GB free`);
|
|
5096
|
-
info.push(`Node: ${process.version}`);
|
|
5097
|
-
if (isTermux()) info.push("Platform: Termux (Android)");
|
|
5098
|
-
if (isCodespaces()) info.push("Platform: GitHub Codespaces");
|
|
5099
|
-
return info;
|
|
5100
|
-
}
|
|
5101
|
-
|
|
5102
|
-
// src/platform/optimize.ts
|
|
5195
|
+
import chalk26 from "chalk";
|
|
5103
5196
|
function formatPlatformStatus() {
|
|
5104
|
-
const lines = [
|
|
5105
|
-
lines.push(` Termux:
|
|
5106
|
-
lines.push(`
|
|
5107
|
-
lines.push(`
|
|
5108
|
-
lines.push(`
|
|
5197
|
+
const lines = [chalk26.bold("\n Platform Status")];
|
|
5198
|
+
lines.push(` Termux: ${isTermux() ? chalk26.green("\u2713") : chalk26.dim("\u2014")}`);
|
|
5199
|
+
lines.push(` Touch: ${isTouchDevice() ? chalk26.green("\u2713") : chalk26.dim("\u2014")}`);
|
|
5200
|
+
lines.push(` Codespaces: ${isCodespaces() ? chalk26.green("\u2713") : chalk26.dim("\u2014")}`);
|
|
5201
|
+
lines.push(` Low RAM: ${lowRamMode() ? chalk26.yellow("active") : chalk26.dim("no")}`);
|
|
5202
|
+
lines.push(` Max Mem: ${recommendedMaxMemory()}MB`);
|
|
5109
5203
|
lines.push("");
|
|
5204
|
+
if (isTermux()) {
|
|
5205
|
+
lines.push(chalk26.bold(" Termux Details:"));
|
|
5206
|
+
for (const t of termuxInfo()) {
|
|
5207
|
+
lines.push(` ${t}`);
|
|
5208
|
+
}
|
|
5209
|
+
lines.push("");
|
|
5210
|
+
}
|
|
5110
5211
|
for (const line of platformInfo()) {
|
|
5111
|
-
lines.push(` ${
|
|
5212
|
+
lines.push(` ${chalk26.dim(line)}`);
|
|
5213
|
+
}
|
|
5214
|
+
if (isTouchDevice()) {
|
|
5215
|
+
lines.push("");
|
|
5216
|
+
lines.push(chalk26.dim(" Tip: Use numbered selections instead of arrow keys"));
|
|
5112
5217
|
}
|
|
5113
5218
|
return lines.join("\n");
|
|
5114
5219
|
}
|
|
@@ -5121,14 +5226,14 @@ var platformCommand = new Command6("platform").alias("sys").alias("info").descri
|
|
|
5121
5226
|
|
|
5122
5227
|
// src/commands/telemetry.ts
|
|
5123
5228
|
import { Command as Command7 } from "commander";
|
|
5124
|
-
import
|
|
5229
|
+
import chalk28 from "chalk";
|
|
5125
5230
|
|
|
5126
5231
|
// src/telemetry/telemetry.ts
|
|
5127
5232
|
import * as fs18 from "fs";
|
|
5128
5233
|
import * as path17 from "path";
|
|
5129
5234
|
import * as os2 from "os";
|
|
5130
5235
|
import { createRequire as createRequire2 } from "module";
|
|
5131
|
-
import
|
|
5236
|
+
import chalk27 from "chalk";
|
|
5132
5237
|
var _require2 = createRequire2(import.meta.url);
|
|
5133
5238
|
var TELEMETRY_DIR = ".lovecode/telemetry";
|
|
5134
5239
|
var _enabled = null;
|
|
@@ -5209,16 +5314,16 @@ function clearTelemetryData(rootDir) {
|
|
|
5209
5314
|
}
|
|
5210
5315
|
}
|
|
5211
5316
|
function formatTelemetryStatus(enabled, data) {
|
|
5212
|
-
const lines = [
|
|
5213
|
-
lines.push(` Status: ${enabled ?
|
|
5317
|
+
const lines = [chalk27.bold("\n Telemetry Status")];
|
|
5318
|
+
lines.push(` Status: ${enabled ? chalk27.yellow("ENABLED") : chalk27.green("DISABLED")}`);
|
|
5214
5319
|
lines.push(` Events: ${data.events.length}`);
|
|
5215
5320
|
lines.push(` Crashes: ${data.crashes.length}`);
|
|
5216
5321
|
if (data.events.length > 0) {
|
|
5217
|
-
lines.push(
|
|
5322
|
+
lines.push(chalk27.dim(`
|
|
5218
5323
|
Recent Events:`));
|
|
5219
5324
|
for (const e of data.events.slice(-5)) {
|
|
5220
5325
|
const date = new Date(e.timestamp).toLocaleString();
|
|
5221
|
-
lines.push(` ${
|
|
5326
|
+
lines.push(` ${chalk27.dim(date)} ${e.event}`);
|
|
5222
5327
|
}
|
|
5223
5328
|
}
|
|
5224
5329
|
return lines.join("\n");
|
|
@@ -5232,17 +5337,17 @@ async function cmdTelemetryStatus(options) {
|
|
|
5232
5337
|
}
|
|
5233
5338
|
async function cmdTelemetryEnable(options) {
|
|
5234
5339
|
enableTelemetry(options.dir);
|
|
5235
|
-
console.log(
|
|
5236
|
-
console.log(
|
|
5237
|
-
console.log(
|
|
5340
|
+
console.log(chalk28.yellow("Telemetry enabled."));
|
|
5341
|
+
console.log(chalk28.dim(" Anonymous usage data will be collected to improve LoveCode."));
|
|
5342
|
+
console.log(chalk28.dim(" No personal or project data is ever sent."));
|
|
5238
5343
|
}
|
|
5239
5344
|
async function cmdTelemetryDisable(options) {
|
|
5240
5345
|
disableTelemetry(options.dir);
|
|
5241
|
-
console.log(
|
|
5346
|
+
console.log(chalk28.green("Telemetry disabled."));
|
|
5242
5347
|
}
|
|
5243
5348
|
async function cmdTelemetryClear(options) {
|
|
5244
5349
|
clearTelemetryData(options.dir);
|
|
5245
|
-
console.log(
|
|
5350
|
+
console.log(chalk28.green("Telemetry data cleared."));
|
|
5246
5351
|
}
|
|
5247
5352
|
var telemetryCommand = new Command7("telemetry").alias("analytics").description("Manage anonymous telemetry and crash reporting").option("--dir <path>", "Project directory", process.cwd()).addHelpText("after", `
|
|
5248
5353
|
Privacy-first by default. Telemetry is disabled unless explicitly enabled.
|
|
@@ -5260,13 +5365,13 @@ telemetryCommand.action(cmdTelemetryStatus);
|
|
|
5260
5365
|
|
|
5261
5366
|
// src/commands/install.ts
|
|
5262
5367
|
import { Command as Command8 } from "commander";
|
|
5263
|
-
import
|
|
5368
|
+
import chalk30 from "chalk";
|
|
5264
5369
|
|
|
5265
5370
|
// src/installers/install.ts
|
|
5266
5371
|
import * as fs19 from "fs";
|
|
5267
5372
|
import * as path18 from "path";
|
|
5268
5373
|
import { execSync as execSync4 } from "child_process";
|
|
5269
|
-
import
|
|
5374
|
+
import chalk29 from "chalk";
|
|
5270
5375
|
function detectInstallMethod() {
|
|
5271
5376
|
if (process.env.npm_config_user_agent?.startsWith("npm")) return "npm";
|
|
5272
5377
|
if (process.env.npm_config_user_agent?.startsWith("yarn")) return "yarn";
|
|
@@ -5292,16 +5397,26 @@ function isGloballyInstalled() {
|
|
|
5292
5397
|
}
|
|
5293
5398
|
}
|
|
5294
5399
|
function printInstallInstructions() {
|
|
5295
|
-
console.log(
|
|
5296
|
-
console.log(
|
|
5297
|
-
console.log(` ${
|
|
5400
|
+
console.log(chalk29.bold.cyan("\n LoveCode AI - Installation\n"));
|
|
5401
|
+
console.log(chalk29.bold(" Quick Install:\n"));
|
|
5402
|
+
console.log(` ${chalk29.green("NPM:")}`);
|
|
5298
5403
|
console.log(` npm install -g lovecode-ai
|
|
5299
5404
|
`);
|
|
5300
|
-
console.log(` ${
|
|
5405
|
+
console.log(` ${chalk29.green("Curl:")}`);
|
|
5301
5406
|
console.log(` curl -fsSL https://lovecode.sh | bash
|
|
5302
5407
|
`);
|
|
5303
|
-
console.log(` ${
|
|
5408
|
+
console.log(` ${chalk29.green("Verify:")}`);
|
|
5304
5409
|
console.log(` lovecode --version
|
|
5410
|
+
`);
|
|
5411
|
+
console.log(chalk29.bold(" Termux (Android):\n"));
|
|
5412
|
+
console.log(` pkg install nodejs`);
|
|
5413
|
+
console.log(` npm install -g lovecode-ai`);
|
|
5414
|
+
console.log(` lovecode init
|
|
5415
|
+
`);
|
|
5416
|
+
console.log(` ${chalk29.dim("LoveCode auto-detects Termux and enables:")}`);
|
|
5417
|
+
console.log(` ${chalk29.dim(" \u2022 Low RAM mode (128MB max)")}`);
|
|
5418
|
+
console.log(` ${chalk29.dim(" \u2022 Touch-optimized input")}`);
|
|
5419
|
+
console.log(` ${chalk29.dim(" \u2022 Reduced cache TTL (120s)")}
|
|
5305
5420
|
`);
|
|
5306
5421
|
}
|
|
5307
5422
|
function createInstallScript(rootDir) {
|
|
@@ -5374,20 +5489,20 @@ echo ""
|
|
|
5374
5489
|
async function cmdInstallStatus() {
|
|
5375
5490
|
const info = getInstallerInfo();
|
|
5376
5491
|
const global = isGloballyInstalled();
|
|
5377
|
-
console.log(
|
|
5378
|
-
console.log(` Method: ${
|
|
5492
|
+
console.log(chalk30.bold("\n Install Status"));
|
|
5493
|
+
console.log(` Method: ${chalk30.cyan(info.method)}`);
|
|
5379
5494
|
console.log(` Version: ${info.version}`);
|
|
5380
5495
|
console.log(` Node: ${info.nodeVersion}`);
|
|
5381
5496
|
console.log(` Platform: ${info.platform}`);
|
|
5382
|
-
console.log(` Global CLI: ${global ?
|
|
5497
|
+
console.log(` Global CLI: ${global ? chalk30.green("\u2713") : chalk30.dim("\u2014")}`);
|
|
5383
5498
|
}
|
|
5384
5499
|
async function cmdInstallGuide() {
|
|
5385
5500
|
printInstallInstructions();
|
|
5386
5501
|
}
|
|
5387
5502
|
async function cmdInstallScript(options) {
|
|
5388
5503
|
createInstallScript(options.dir);
|
|
5389
|
-
console.log(
|
|
5390
|
-
console.log(
|
|
5504
|
+
console.log(chalk30.green(` \u2713 install.sh created in ${options.dir || process.cwd()}`));
|
|
5505
|
+
console.log(chalk30.dim(" Run: bash install.sh"));
|
|
5391
5506
|
}
|
|
5392
5507
|
var installCommand = new Command8("install").alias("installer").description("Installation management and instructions").option("--dir <path>", "Project directory", process.cwd()).addHelpText("after", `
|
|
5393
5508
|
Examples:
|
|
@@ -5401,7 +5516,7 @@ installCommand.action(cmdInstallStatus);
|
|
|
5401
5516
|
|
|
5402
5517
|
// src/commands/models.ts
|
|
5403
5518
|
import { Command as Command9 } from "commander";
|
|
5404
|
-
import
|
|
5519
|
+
import chalk31 from "chalk";
|
|
5405
5520
|
var modelsCommand = new Command9("models").description("Manage AI models and providers").addCommand(
|
|
5406
5521
|
new Command9("list").alias("ls").description("List all available models and providers").action(() => {
|
|
5407
5522
|
console.log(printProviders());
|
|
@@ -5410,8 +5525,8 @@ var modelsCommand = new Command9("models").description("Manage AI models and pro
|
|
|
5410
5525
|
new Command9("use").description("Set the default model to use").argument("<model>", "Model name or provider name").action((model) => {
|
|
5411
5526
|
const result = setDefaultModel(model);
|
|
5412
5527
|
if (result) {
|
|
5413
|
-
console.log(
|
|
5414
|
-
\u2713 Default set to ${
|
|
5528
|
+
console.log(chalk31.green(`
|
|
5529
|
+
\u2713 Default set to ${chalk31.cyan(result.provider)}/${chalk31.cyan(result.model)}
|
|
5415
5530
|
`));
|
|
5416
5531
|
}
|
|
5417
5532
|
})
|
|
@@ -5420,20 +5535,20 @@ var modelsCommand = new Command9("models").description("Manage AI models and pro
|
|
|
5420
5535
|
if (provider) {
|
|
5421
5536
|
const entry = getProvider(provider);
|
|
5422
5537
|
if (!entry) {
|
|
5423
|
-
console.log(
|
|
5538
|
+
console.log(chalk31.red(`
|
|
5424
5539
|
Unknown provider: "${provider}"
|
|
5425
5540
|
`));
|
|
5426
5541
|
return;
|
|
5427
5542
|
}
|
|
5428
|
-
const tag = entry.local ?
|
|
5543
|
+
const tag = entry.local ? chalk31.green(" LOCAL ") : chalk31.blue(" CLOUD ");
|
|
5429
5544
|
console.log(`
|
|
5430
|
-
${tag} ${
|
|
5431
|
-
console.log(
|
|
5432
|
-
console.log(
|
|
5433
|
-
console.log(
|
|
5545
|
+
${tag} ${chalk31.cyan(entry.name)}`);
|
|
5546
|
+
console.log(chalk31.dim(` Default model: ${entry.defaultModel}`));
|
|
5547
|
+
console.log(chalk31.dim(` Priority: ${entry.priority}`));
|
|
5548
|
+
console.log(chalk31.dim(` Models:`));
|
|
5434
5549
|
for (const m of entry.models) {
|
|
5435
5550
|
const isDefault = m === entry.defaultModel;
|
|
5436
|
-
console.log(` ${isDefault ?
|
|
5551
|
+
console.log(` ${isDefault ? chalk31.green("\u2605") : " "} ${m}${isDefault ? chalk31.dim(" (default)") : ""}`);
|
|
5437
5552
|
}
|
|
5438
5553
|
console.log("");
|
|
5439
5554
|
} else {
|
|
@@ -5444,13 +5559,13 @@ var modelsCommand = new Command9("models").description("Manage AI models and pro
|
|
|
5444
5559
|
new Command9("test").description("Test a provider connection").argument("[provider]", "Provider name to test", "ollama").option("-m, --model <name>", "Model to test with").action(async (provider, options) => {
|
|
5445
5560
|
const entry = getProvider(provider);
|
|
5446
5561
|
if (!entry) {
|
|
5447
|
-
console.log(
|
|
5562
|
+
console.log(chalk31.red(`
|
|
5448
5563
|
Unknown provider: "${provider}"
|
|
5449
5564
|
`));
|
|
5450
5565
|
return;
|
|
5451
5566
|
}
|
|
5452
5567
|
const model = options.model || entry.defaultModel;
|
|
5453
|
-
console.log(
|
|
5568
|
+
console.log(chalk31.dim(`
|
|
5454
5569
|
Testing ${entry.name}/${model}...`));
|
|
5455
5570
|
try {
|
|
5456
5571
|
const config = entry.getConfig?.(model) || {
|
|
@@ -5463,9 +5578,9 @@ var modelsCommand = new Command9("models").description("Manage AI models and pro
|
|
|
5463
5578
|
[{ role: "user", content: "Reply with exactly: OK" }],
|
|
5464
5579
|
config
|
|
5465
5580
|
);
|
|
5466
|
-
console.log(
|
|
5581
|
+
console.log(chalk31.green(` \u2713 ${entry.name}/${model} responded: ${result.slice(0, 50)}`));
|
|
5467
5582
|
} catch (err) {
|
|
5468
|
-
console.log(
|
|
5583
|
+
console.log(chalk31.red(` \u2717 ${entry.name}/${model} failed: ${err.message}`));
|
|
5469
5584
|
}
|
|
5470
5585
|
console.log("");
|
|
5471
5586
|
})
|
|
@@ -5473,12 +5588,12 @@ var modelsCommand = new Command9("models").description("Manage AI models and pro
|
|
|
5473
5588
|
|
|
5474
5589
|
// src/commands/analyze.ts
|
|
5475
5590
|
import { Command as Command10 } from "commander";
|
|
5476
|
-
import
|
|
5591
|
+
import chalk33 from "chalk";
|
|
5477
5592
|
|
|
5478
5593
|
// src/repo/search.ts
|
|
5479
5594
|
import * as fs20 from "fs";
|
|
5480
5595
|
import * as path19 from "path";
|
|
5481
|
-
import
|
|
5596
|
+
import chalk32 from "chalk";
|
|
5482
5597
|
function chunkFile(content, maxLines = 50) {
|
|
5483
5598
|
const lines = content.split("\n");
|
|
5484
5599
|
const chunks = [];
|
|
@@ -5611,23 +5726,23 @@ function readFileSafe(filePath) {
|
|
|
5611
5726
|
}
|
|
5612
5727
|
}
|
|
5613
5728
|
function printSemanticResults(results, query) {
|
|
5614
|
-
const lines = [
|
|
5729
|
+
const lines = [chalk32.bold(`
|
|
5615
5730
|
Semantic Search: "${query}"`), ""];
|
|
5616
5731
|
if (results.length === 0) {
|
|
5617
|
-
lines.push(
|
|
5732
|
+
lines.push(chalk32.dim(" No results found."));
|
|
5618
5733
|
lines.push("");
|
|
5619
5734
|
return lines.join("\n");
|
|
5620
5735
|
}
|
|
5621
5736
|
for (let i = 0; i < results.length; i++) {
|
|
5622
5737
|
const r = results[i];
|
|
5623
5738
|
const barLen = Math.round(r.score * 30);
|
|
5624
|
-
const bar =
|
|
5625
|
-
lines.push(` ${
|
|
5626
|
-
lines.push(` ${bar} ${
|
|
5739
|
+
const bar = chalk32.cyan("\u2588".repeat(barLen)) + chalk32.dim("\u2591".repeat(30 - barLen));
|
|
5740
|
+
lines.push(` ${chalk32.cyan(String(i + 1).padEnd(3))} ${chalk32.dim(r.relativePath)}`);
|
|
5741
|
+
lines.push(` ${bar} ${chalk32.bold(String(Math.round(r.score * 100)))}%`);
|
|
5627
5742
|
if (r.matches.length > 0) {
|
|
5628
5743
|
const top = r.matches.slice(0, 2);
|
|
5629
5744
|
for (const m of top) {
|
|
5630
|
-
lines.push(` ${
|
|
5745
|
+
lines.push(` ${chalk32.dim(`L${m.line}:`)} ${m.content.slice(0, 100)}`);
|
|
5631
5746
|
}
|
|
5632
5747
|
}
|
|
5633
5748
|
}
|
|
@@ -5643,27 +5758,27 @@ var analyzeCommand = new Command10("analyze").alias("a").description("Analyze an
|
|
|
5643
5758
|
})
|
|
5644
5759
|
).addCommand(
|
|
5645
5760
|
new Command10("deps").alias("d").description("Analyze dependencies and import graph").option("--dir <path>", "Project directory", process.cwd()).action((options) => {
|
|
5646
|
-
console.log(
|
|
5761
|
+
console.log(chalk33.dim("\n Analyzing dependencies..."));
|
|
5647
5762
|
const graph = analyzeDependencies(options.dir);
|
|
5648
5763
|
console.log(printDepGraph(graph));
|
|
5649
5764
|
const circles = findCircularDeps(graph);
|
|
5650
5765
|
if (circles.length > 0) {
|
|
5651
|
-
console.log(
|
|
5766
|
+
console.log(chalk33.yellow(` \u26A0 Found ${circles.length} circular dependenc${circles.length > 1 ? "ies" : "y"}`));
|
|
5652
5767
|
for (const circle of circles.slice(0, 5)) {
|
|
5653
|
-
console.log(` ${
|
|
5768
|
+
console.log(` ${chalk33.red("\u21BB")} ${circle.join(" \u2192 ")}`);
|
|
5654
5769
|
}
|
|
5655
5770
|
console.log("");
|
|
5656
5771
|
}
|
|
5657
5772
|
})
|
|
5658
5773
|
).addCommand(
|
|
5659
5774
|
new Command10("summary").alias("s").description("Generate a full repo architecture summary").option("--dir <path>", "Project directory", process.cwd()).action((options) => {
|
|
5660
|
-
console.log(
|
|
5775
|
+
console.log(chalk33.dim("\n Generating repository summary...\n"));
|
|
5661
5776
|
const summary = generateSummary(options.dir);
|
|
5662
5777
|
console.log(printSummary(summary));
|
|
5663
5778
|
})
|
|
5664
5779
|
).addCommand(
|
|
5665
5780
|
new Command10("search").alias("q").description("Semantically search code in the repository").argument("<query>", "The search query").option("--dir <path>", "Project directory", process.cwd()).option("--max <number>", "Maximum results", "10").action(async (query, options) => {
|
|
5666
|
-
console.log(
|
|
5781
|
+
console.log(chalk33.dim(`
|
|
5667
5782
|
Searching for: "${query}"
|
|
5668
5783
|
`));
|
|
5669
5784
|
const results = await semanticSearch2(options.dir, query, {
|
|
@@ -5689,36 +5804,36 @@ var analyzeCommand = new Command10("analyze").alias("a").description("Analyze an
|
|
|
5689
5804
|
|
|
5690
5805
|
// src/commands/memory.ts
|
|
5691
5806
|
import { Command as Command11 } from "commander";
|
|
5692
|
-
import
|
|
5807
|
+
import chalk34 from "chalk";
|
|
5693
5808
|
async function cmdSessions(options) {
|
|
5694
5809
|
const sessions = listSessions(options.dir);
|
|
5695
5810
|
if (sessions.length === 0) {
|
|
5696
|
-
console.log(
|
|
5811
|
+
console.log(chalk34.yellow("No sessions found."));
|
|
5697
5812
|
return;
|
|
5698
5813
|
}
|
|
5699
|
-
console.log(
|
|
5814
|
+
console.log(chalk34.bold(`
|
|
5700
5815
|
Sessions (${sessions.length}):
|
|
5701
5816
|
`));
|
|
5702
5817
|
for (const s of sessions) {
|
|
5703
5818
|
const date = new Date(s.updated).toLocaleString();
|
|
5704
5819
|
const msgCount = s.entries.length;
|
|
5705
|
-
console.log(` ${
|
|
5706
|
-
console.log(` ${
|
|
5820
|
+
console.log(` ${chalk34.cyan(s.id.slice(0, 12))} ${chalk34.bold(s.title)}`);
|
|
5821
|
+
console.log(` ${chalk34.dim(`${msgCount} msgs \u2022 ${date} \u2022 ${s.model}`)}`);
|
|
5707
5822
|
}
|
|
5708
5823
|
}
|
|
5709
5824
|
async function cmdShowSession(id, options) {
|
|
5710
5825
|
const session = loadSession(id, options.dir);
|
|
5711
5826
|
if (!session) {
|
|
5712
|
-
console.log(
|
|
5827
|
+
console.log(chalk34.red(`Session "${id}" not found.`));
|
|
5713
5828
|
return;
|
|
5714
5829
|
}
|
|
5715
|
-
console.log(
|
|
5830
|
+
console.log(chalk34.bold(`
|
|
5716
5831
|
Session: ${session.title}`));
|
|
5717
|
-
console.log(
|
|
5718
|
-
console.log(
|
|
5832
|
+
console.log(chalk34.dim(` ID: ${session.id} \u2022 Model: ${session.model} \u2022 ${session.entries.length} messages`));
|
|
5833
|
+
console.log(chalk34.dim(` Created: ${new Date(session.created).toLocaleString()}`));
|
|
5719
5834
|
console.log("");
|
|
5720
5835
|
for (const entry of session.entries.slice(-20)) {
|
|
5721
|
-
const role = entry.role === "user" ?
|
|
5836
|
+
const role = entry.role === "user" ? chalk34.green("You") : entry.role === "assistant" ? chalk34.cyan("LoveCode") : chalk34.yellow("System");
|
|
5722
5837
|
const preview = entry.content.length > 200 ? entry.content.slice(0, 200) + "..." : entry.content;
|
|
5723
5838
|
console.log(` ${role}: ${preview}
|
|
5724
5839
|
`);
|
|
@@ -5727,15 +5842,15 @@ async function cmdShowSession(id, options) {
|
|
|
5727
5842
|
async function cmdSearchSessions(query, options) {
|
|
5728
5843
|
const results = searchSessions(query, options.dir);
|
|
5729
5844
|
if (results.length === 0) {
|
|
5730
|
-
console.log(
|
|
5845
|
+
console.log(chalk34.yellow(`No sessions matching "${query}".`));
|
|
5731
5846
|
return;
|
|
5732
5847
|
}
|
|
5733
|
-
console.log(
|
|
5848
|
+
console.log(chalk34.bold(`
|
|
5734
5849
|
Search results for "${query}" (${results.length}):
|
|
5735
5850
|
`));
|
|
5736
5851
|
for (const s of results) {
|
|
5737
5852
|
const date = new Date(s.updated).toLocaleString();
|
|
5738
|
-
console.log(` ${
|
|
5853
|
+
console.log(` ${chalk34.cyan(s.id.slice(0, 12))} ${chalk34.bold(s.title)} ${chalk34.dim(date)}`);
|
|
5739
5854
|
}
|
|
5740
5855
|
}
|
|
5741
5856
|
async function cmdPreferences(options) {
|
|
@@ -5755,7 +5870,7 @@ async function cmdPreferences(options) {
|
|
|
5755
5870
|
else prefs[k] = v;
|
|
5756
5871
|
}
|
|
5757
5872
|
const saved = savePreferences(prefs, options.dir);
|
|
5758
|
-
console.log(
|
|
5873
|
+
console.log(chalk34.green("Preferences updated:"));
|
|
5759
5874
|
console.log(formatPreferences(saved));
|
|
5760
5875
|
} else {
|
|
5761
5876
|
const prefs = getPreferences(options.dir);
|
|
@@ -5765,7 +5880,7 @@ async function cmdPreferences(options) {
|
|
|
5765
5880
|
async function cmdRepoMemory(options) {
|
|
5766
5881
|
if (options.note) {
|
|
5767
5882
|
const updated = addRepoNote(options.note, options.dir);
|
|
5768
|
-
console.log(
|
|
5883
|
+
console.log(chalk34.green("Note added to repo memory."));
|
|
5769
5884
|
console.log(formatRepoMemory(updated));
|
|
5770
5885
|
} else {
|
|
5771
5886
|
const mem = getRepoMemory(options.dir);
|
|
@@ -5785,31 +5900,31 @@ async function cmdWorkflows(options) {
|
|
|
5785
5900
|
used: Date.now()
|
|
5786
5901
|
};
|
|
5787
5902
|
saveWorkflow(workflow, options.dir);
|
|
5788
|
-
console.log(
|
|
5903
|
+
console.log(chalk34.green(`Workflow "${options.name}" saved (${steps.length} steps).`));
|
|
5789
5904
|
return;
|
|
5790
5905
|
}
|
|
5791
5906
|
if (options.delete) {
|
|
5792
5907
|
const ok = deleteWorkflow(options.delete, options.dir);
|
|
5793
|
-
console.log(ok ?
|
|
5908
|
+
console.log(ok ? chalk34.green(`Workflow "${options.delete}" deleted.`) : chalk34.yellow(`Workflow "${options.delete}" not found.`));
|
|
5794
5909
|
return;
|
|
5795
5910
|
}
|
|
5796
5911
|
if (wf.workflows.length === 0) {
|
|
5797
|
-
console.log(
|
|
5912
|
+
console.log(chalk34.yellow("No saved workflows."));
|
|
5798
5913
|
return;
|
|
5799
5914
|
}
|
|
5800
|
-
console.log(
|
|
5915
|
+
console.log(chalk34.bold(`
|
|
5801
5916
|
Workflows (${wf.workflows.length}):
|
|
5802
5917
|
`));
|
|
5803
5918
|
for (const w of wf.workflows) {
|
|
5804
5919
|
const date = new Date(w.used).toLocaleString();
|
|
5805
|
-
console.log(` ${
|
|
5806
|
-
console.log(` ${
|
|
5920
|
+
console.log(` ${chalk34.cyan(w.name)} ${chalk34.dim(w.description || "(no description)")}`);
|
|
5921
|
+
console.log(` ${chalk34.dim(`${w.steps.length} steps \u2022 last used ${date}`)}`);
|
|
5807
5922
|
}
|
|
5808
5923
|
}
|
|
5809
5924
|
async function cmdVectorStore(options) {
|
|
5810
5925
|
if (options.clear) {
|
|
5811
5926
|
clearVectors(options.dir);
|
|
5812
|
-
console.log(
|
|
5927
|
+
console.log(chalk34.green("Vector memory cleared."));
|
|
5813
5928
|
return;
|
|
5814
5929
|
}
|
|
5815
5930
|
if (options.count) {
|
|
@@ -5818,8 +5933,8 @@ async function cmdVectorStore(options) {
|
|
|
5818
5933
|
}
|
|
5819
5934
|
if (options.store) {
|
|
5820
5935
|
const entry = await storeVector(options.store, {}, options.dir);
|
|
5821
|
-
console.log(
|
|
5822
|
-
console.log(
|
|
5936
|
+
console.log(chalk34.green(`Stored: "${options.store.slice(0, 80)}..."`));
|
|
5937
|
+
console.log(chalk34.dim(` ID: ${entry.id}`));
|
|
5823
5938
|
return;
|
|
5824
5939
|
}
|
|
5825
5940
|
if (options.query) {
|
|
@@ -5827,32 +5942,32 @@ async function cmdVectorStore(options) {
|
|
|
5827
5942
|
console.log(formatVectorResults(results));
|
|
5828
5943
|
return;
|
|
5829
5944
|
}
|
|
5830
|
-
console.log(
|
|
5945
|
+
console.log(chalk34.yellow("Specify --query, --store, --count, or --clear."));
|
|
5831
5946
|
}
|
|
5832
5947
|
async function cmdChatLogs(options) {
|
|
5833
5948
|
const logs = listChatLogs(options.dir);
|
|
5834
5949
|
if (logs.length === 0) {
|
|
5835
|
-
console.log(
|
|
5950
|
+
console.log(chalk34.yellow("No chat logs found."));
|
|
5836
5951
|
return;
|
|
5837
5952
|
}
|
|
5838
5953
|
const totalSize = logs.reduce((acc, l) => acc + l.size, 0);
|
|
5839
5954
|
const sizeStr = totalSize > 1024 ? `${(totalSize / 1024).toFixed(1)} KB` : `${totalSize} B`;
|
|
5840
|
-
console.log(
|
|
5955
|
+
console.log(chalk34.bold(`
|
|
5841
5956
|
Chat Logs (${logs.length}, ${sizeStr}):
|
|
5842
5957
|
`));
|
|
5843
5958
|
for (const log of logs.slice(0, 20)) {
|
|
5844
5959
|
const date = log.modified.toLocaleString();
|
|
5845
5960
|
const size = log.size > 1024 ? `${(log.size / 1024).toFixed(1)} KB` : `${log.size} B`;
|
|
5846
|
-
console.log(` ${
|
|
5961
|
+
console.log(` ${chalk34.dim(log.name.slice(0, 50).padEnd(52))} ${chalk34.yellow(size.padStart(8))} ${chalk34.dim(date)}`);
|
|
5847
5962
|
}
|
|
5848
5963
|
if (logs.length > 20) {
|
|
5849
|
-
console.log(
|
|
5964
|
+
console.log(chalk34.dim(` ... and ${logs.length - 20} more`));
|
|
5850
5965
|
}
|
|
5851
5966
|
}
|
|
5852
5967
|
async function cmdClearAll(options) {
|
|
5853
5968
|
clearAllMemory(options.dir);
|
|
5854
5969
|
clearVectors(options.dir);
|
|
5855
|
-
console.log(
|
|
5970
|
+
console.log(chalk34.green("All memory data cleared."));
|
|
5856
5971
|
}
|
|
5857
5972
|
var memoryCommand = new Command11("memory").alias("mem").description("Manage sessions, memory, and chat logs").option("--dir <path>", "Project directory", process.cwd()).addCommand(
|
|
5858
5973
|
new Command11("sessions").alias("s").description("List all persistent sessions").option("--dir <path>", "Project directory").action(cmdSessions)
|
|
@@ -5894,14 +6009,14 @@ var memoryCommand = new Command11("memory").alias("mem").description("Manage ses
|
|
|
5894
6009
|
|
|
5895
6010
|
// src/commands/git.ts
|
|
5896
6011
|
import { Command as Command12 } from "commander";
|
|
5897
|
-
import
|
|
6012
|
+
import chalk35 from "chalk";
|
|
5898
6013
|
function requireGit(cwd) {
|
|
5899
6014
|
if (!isGitAvailable()) {
|
|
5900
|
-
console.log(
|
|
6015
|
+
console.log(chalk35.red(" \u2717 Git is not installed or not in PATH."));
|
|
5901
6016
|
return false;
|
|
5902
6017
|
}
|
|
5903
6018
|
if (!isRepo(cwd)) {
|
|
5904
|
-
console.log(
|
|
6019
|
+
console.log(chalk35.red(" \u2717 Not a git repository."));
|
|
5905
6020
|
return false;
|
|
5906
6021
|
}
|
|
5907
6022
|
return true;
|
|
@@ -5915,39 +6030,39 @@ async function cmdCommit(opts) {
|
|
|
5915
6030
|
}
|
|
5916
6031
|
const status = getStatus(root || dir);
|
|
5917
6032
|
if (status.clean && !opts.message) {
|
|
5918
|
-
console.log(
|
|
6033
|
+
console.log(chalk35.yellow(" Nothing to commit. Working tree clean."));
|
|
5919
6034
|
return;
|
|
5920
6035
|
}
|
|
5921
6036
|
let commitMessage = opts.message;
|
|
5922
6037
|
if (!commitMessage && opts.generate) {
|
|
5923
|
-
console.log(
|
|
6038
|
+
console.log(chalk35.dim(" Generating commit message..."));
|
|
5924
6039
|
commitMessage = await generateCommitMessage();
|
|
5925
|
-
console.log(
|
|
6040
|
+
console.log(chalk35.cyan(` Generated: ${commitMessage.split("\n")[0]}
|
|
5926
6041
|
`));
|
|
5927
6042
|
}
|
|
5928
6043
|
if (!commitMessage) {
|
|
5929
|
-
console.log(
|
|
5930
|
-
console.log(
|
|
5931
|
-
console.log(
|
|
5932
|
-
console.log(
|
|
6044
|
+
console.log(chalk35.yellow(" No commit message provided. Use --message or --generate."));
|
|
6045
|
+
console.log(chalk35.dim(" Examples:"));
|
|
6046
|
+
console.log(chalk35.dim(' lovecode commit -m "feat: add login"'));
|
|
6047
|
+
console.log(chalk35.dim(" lovecode commit --generate"));
|
|
5933
6048
|
return;
|
|
5934
6049
|
}
|
|
5935
6050
|
if (opts.all) {
|
|
5936
|
-
console.log(
|
|
6051
|
+
console.log(chalk35.dim(" Staging all changes..."));
|
|
5937
6052
|
}
|
|
5938
6053
|
const result = commit(commitMessage, root || dir);
|
|
5939
6054
|
if (result.success) {
|
|
5940
|
-
console.log(
|
|
6055
|
+
console.log(chalk35.green(` \u2713 Committed${result.hash ? ` (${result.hash.slice(0, 8)})` : ""}`));
|
|
5941
6056
|
if (result.output) {
|
|
5942
6057
|
const lines = result.output.split("\n").filter(Boolean);
|
|
5943
6058
|
for (const line of lines) {
|
|
5944
6059
|
if (line.includes("changed") || line.includes("insertion") || line.includes("deletion")) {
|
|
5945
|
-
console.log(
|
|
6060
|
+
console.log(chalk35.dim(` ${line}`));
|
|
5946
6061
|
}
|
|
5947
6062
|
}
|
|
5948
6063
|
}
|
|
5949
6064
|
} else {
|
|
5950
|
-
console.log(
|
|
6065
|
+
console.log(chalk35.red(` \u2717 ${result.output}`));
|
|
5951
6066
|
}
|
|
5952
6067
|
}
|
|
5953
6068
|
async function cmdStatus(options) {
|
|
@@ -5955,8 +6070,8 @@ async function cmdStatus(options) {
|
|
|
5955
6070
|
const dir = options.dir || process.cwd();
|
|
5956
6071
|
const root = getGitRoot(dir);
|
|
5957
6072
|
const status = getStatus(root || dir);
|
|
5958
|
-
console.log(
|
|
5959
|
-
Git Status \u2014 ${
|
|
6073
|
+
console.log(chalk35.bold(`
|
|
6074
|
+
Git Status \u2014 ${chalk35.cyan(status.branch)}`));
|
|
5960
6075
|
console.log(` ${formatStatus(status)}`);
|
|
5961
6076
|
console.log("");
|
|
5962
6077
|
}
|
|
@@ -5966,39 +6081,39 @@ async function cmdBranch(action, name, options) {
|
|
|
5966
6081
|
const gitRoot = getGitRoot(dir);
|
|
5967
6082
|
if (!action || action === "list") {
|
|
5968
6083
|
const branches = getBranches(gitRoot || dir);
|
|
5969
|
-
console.log(
|
|
6084
|
+
console.log(chalk35.bold(`
|
|
5970
6085
|
Branches (${branches.length}):`));
|
|
5971
6086
|
console.log(` ${formatBranches(branches)}`);
|
|
5972
6087
|
console.log("");
|
|
5973
6088
|
return;
|
|
5974
6089
|
}
|
|
5975
6090
|
if (!name) {
|
|
5976
|
-
console.log(
|
|
6091
|
+
console.log(chalk35.yellow(" Branch name required."));
|
|
5977
6092
|
return;
|
|
5978
6093
|
}
|
|
5979
6094
|
switch (action) {
|
|
5980
6095
|
case "create": {
|
|
5981
6096
|
const ok = createBranch(name, gitRoot || dir);
|
|
5982
|
-
console.log(ok ?
|
|
6097
|
+
console.log(ok ? chalk35.green(` \u2713 Created and switched to "${name}"`) : chalk35.red(` \u2717 Failed to create branch "${name}"`));
|
|
5983
6098
|
break;
|
|
5984
6099
|
}
|
|
5985
6100
|
case "switch": {
|
|
5986
6101
|
const result = switchBranch(name, gitRoot || dir);
|
|
5987
|
-
console.log(result.success ?
|
|
6102
|
+
console.log(result.success ? chalk35.green(` \u2713 Switched to "${name}"`) : chalk35.red(` \u2717 ${result.output}`));
|
|
5988
6103
|
break;
|
|
5989
6104
|
}
|
|
5990
6105
|
case "delete": {
|
|
5991
6106
|
const result = deleteBranch(name, options.force, gitRoot || dir);
|
|
5992
|
-
console.log(result.success ?
|
|
6107
|
+
console.log(result.success ? chalk35.green(` \u2713 Deleted branch "${name}"`) : chalk35.red(` \u2717 ${result.output}`));
|
|
5993
6108
|
break;
|
|
5994
6109
|
}
|
|
5995
6110
|
case "cleanup": {
|
|
5996
|
-
console.log(
|
|
6111
|
+
console.log(chalk35.dim(" Cleaning up merged branches..."));
|
|
5997
6112
|
const deleted = cleanupMergedBranches(gitRoot || dir);
|
|
5998
6113
|
if (deleted.length === 0) {
|
|
5999
|
-
console.log(
|
|
6114
|
+
console.log(chalk35.yellow(" No merged branches to clean up."));
|
|
6000
6115
|
} else {
|
|
6001
|
-
console.log(
|
|
6116
|
+
console.log(chalk35.green(` \u2713 Deleted ${deleted.length} merged branc${deleted.length > 1 ? "hes" : "h"}:`));
|
|
6002
6117
|
for (const b of deleted) {
|
|
6003
6118
|
console.log(` \u2022 ${b}`);
|
|
6004
6119
|
}
|
|
@@ -6006,7 +6121,7 @@ async function cmdBranch(action, name, options) {
|
|
|
6006
6121
|
break;
|
|
6007
6122
|
}
|
|
6008
6123
|
default:
|
|
6009
|
-
console.log(
|
|
6124
|
+
console.log(chalk35.yellow(` Unknown action: ${action}. Use: create, switch, delete, cleanup, or list.`));
|
|
6010
6125
|
}
|
|
6011
6126
|
}
|
|
6012
6127
|
async function cmdLog(options) {
|
|
@@ -6016,10 +6131,10 @@ async function cmdLog(options) {
|
|
|
6016
6131
|
const count = parseInt(options.count || "10", 10);
|
|
6017
6132
|
const log = getLog(count, root || dir);
|
|
6018
6133
|
if (log.length === 0) {
|
|
6019
|
-
console.log(
|
|
6134
|
+
console.log(chalk35.yellow(" No commits found."));
|
|
6020
6135
|
return;
|
|
6021
6136
|
}
|
|
6022
|
-
console.log(
|
|
6137
|
+
console.log(chalk35.bold(`
|
|
6023
6138
|
Recent Commits (${log.length}):`));
|
|
6024
6139
|
console.log(` ${formatLog(log)}`);
|
|
6025
6140
|
console.log("");
|
|
@@ -6027,7 +6142,7 @@ async function cmdLog(options) {
|
|
|
6027
6142
|
async function cmdPRSummary(baseBranch, options) {
|
|
6028
6143
|
if (!requireGit(options.dir)) return;
|
|
6029
6144
|
const base = baseBranch || "main";
|
|
6030
|
-
console.log(
|
|
6145
|
+
console.log(chalk35.dim(` Generating PR summary (${base}...HEAD)...`));
|
|
6031
6146
|
const summary = await generatePRSummary(base);
|
|
6032
6147
|
console.log(`
|
|
6033
6148
|
${summary}
|
|
@@ -6053,19 +6168,19 @@ async function cmdDiff(options) {
|
|
|
6053
6168
|
const root = getGitRoot(dir);
|
|
6054
6169
|
const diff = options.staged ? (await import("./git-FZPRJVFI.js")).getStagedDiff(root || dir) : getDiff(root || dir);
|
|
6055
6170
|
if (!diff) {
|
|
6056
|
-
console.log(
|
|
6171
|
+
console.log(chalk35.yellow(" No changes to show."));
|
|
6057
6172
|
return;
|
|
6058
6173
|
}
|
|
6059
6174
|
const lines = diff.split("\n");
|
|
6060
6175
|
for (const line of lines) {
|
|
6061
6176
|
if (line.startsWith("+")) {
|
|
6062
|
-
process.stdout.write(
|
|
6177
|
+
process.stdout.write(chalk35.green(line) + "\n");
|
|
6063
6178
|
} else if (line.startsWith("-")) {
|
|
6064
|
-
process.stdout.write(
|
|
6179
|
+
process.stdout.write(chalk35.red(line) + "\n");
|
|
6065
6180
|
} else if (line.startsWith("@@")) {
|
|
6066
|
-
process.stdout.write(
|
|
6181
|
+
process.stdout.write(chalk35.cyan(line) + "\n");
|
|
6067
6182
|
} else if (line.startsWith("diff --git")) {
|
|
6068
|
-
process.stdout.write(
|
|
6183
|
+
process.stdout.write(chalk35.bold(line) + "\n");
|
|
6069
6184
|
} else {
|
|
6070
6185
|
process.stdout.write(line + "\n");
|
|
6071
6186
|
}
|
|
@@ -6109,7 +6224,7 @@ gitCommand.command("diff").description("Show diff").option("--staged", "Show sta
|
|
|
6109
6224
|
|
|
6110
6225
|
// src/commands/tui.ts
|
|
6111
6226
|
import { Command as Command13 } from "commander";
|
|
6112
|
-
import
|
|
6227
|
+
import chalk36 from "chalk";
|
|
6113
6228
|
|
|
6114
6229
|
// src/tui/index.ts
|
|
6115
6230
|
import React4 from "react";
|
|
@@ -6888,19 +7003,293 @@ function startTUI(props) {
|
|
|
6888
7003
|
}
|
|
6889
7004
|
|
|
6890
7005
|
// src/commands/tui.ts
|
|
7006
|
+
var TOOL_SYSTEM_PROMPT = `You are LoveCode AI, a terminal-native coding assistant with full access to the project filesystem.
|
|
7007
|
+
|
|
7008
|
+
You can read, write, edit, and search files using tool tags:
|
|
7009
|
+
|
|
7010
|
+
<tool name="read_file">
|
|
7011
|
+
path=<file path>
|
|
7012
|
+
</tool>
|
|
7013
|
+
|
|
7014
|
+
<tool name="write_file">
|
|
7015
|
+
path=<file path>
|
|
7016
|
+
content=<file content>
|
|
7017
|
+
</tool>
|
|
7018
|
+
|
|
7019
|
+
<tool name="edit_file">
|
|
7020
|
+
path=<file path>
|
|
7021
|
+
oldString=<text to replace>
|
|
7022
|
+
newString=<replacement text>
|
|
7023
|
+
</tool>
|
|
7024
|
+
|
|
7025
|
+
<tool name="create_file">
|
|
7026
|
+
path=<file path>
|
|
7027
|
+
</tool>
|
|
7028
|
+
|
|
7029
|
+
<tool name="delete_file">
|
|
7030
|
+
path=<file path>
|
|
7031
|
+
</tool>
|
|
7032
|
+
|
|
7033
|
+
<tool name="append_file">
|
|
7034
|
+
path=<file path>
|
|
7035
|
+
content=<text to append>
|
|
7036
|
+
</tool>
|
|
7037
|
+
|
|
7038
|
+
<tool name="grep_search">
|
|
7039
|
+
pattern=<regex pattern>
|
|
7040
|
+
</tool>
|
|
7041
|
+
|
|
7042
|
+
<tool name="glob_search">
|
|
7043
|
+
pattern=<glob pattern>
|
|
7044
|
+
</tool>
|
|
7045
|
+
|
|
7046
|
+
<tool name="read_dir">
|
|
7047
|
+
path=<directory path>
|
|
7048
|
+
</tool>
|
|
7049
|
+
|
|
7050
|
+
<tool name="execute_command">
|
|
7051
|
+
command=<shell command>
|
|
7052
|
+
</tool>
|
|
7053
|
+
|
|
7054
|
+
When you need to access files, use the appropriate tool tag.
|
|
7055
|
+
After getting results, continue helping the user.`;
|
|
7056
|
+
async function loadAIProvider() {
|
|
7057
|
+
try {
|
|
7058
|
+
const { loadEnv: loadEnv2 } = await import("./env-HJQWWL6N.js");
|
|
7059
|
+
const { loadConfig: loadConfig2 } = await import("./config-FJNTTKR3.js");
|
|
7060
|
+
const { resolveModel } = await import("./registry-ADSIKXA4.js");
|
|
7061
|
+
loadEnv2();
|
|
7062
|
+
const cfg = loadConfig2();
|
|
7063
|
+
if (!cfg.provider) {
|
|
7064
|
+
console.log(chalk36.yellow("No AI provider configured. Use `lovecode init` to set one up."));
|
|
7065
|
+
return null;
|
|
7066
|
+
}
|
|
7067
|
+
const resolved = resolveModel(cfg.provider);
|
|
7068
|
+
if (!resolved) {
|
|
7069
|
+
console.log(chalk36.yellow(`Provider "${cfg.provider}" not found. Use \`lovecode init\` to reconfigure.`));
|
|
7070
|
+
return null;
|
|
7071
|
+
}
|
|
7072
|
+
const provider = resolved.entry.provider;
|
|
7073
|
+
const model = resolved.model;
|
|
7074
|
+
const config = {
|
|
7075
|
+
model,
|
|
7076
|
+
temperature: cfg.model_params?.temperature ?? 0.2,
|
|
7077
|
+
maxTokens: cfg.model_params?.max_tokens ?? 4096,
|
|
7078
|
+
baseUrl: cfg.api?.base_url
|
|
7079
|
+
};
|
|
7080
|
+
return { provider, config };
|
|
7081
|
+
} catch (err) {
|
|
7082
|
+
console.log(chalk36.red(`Failed to load AI provider: ${err.message}`));
|
|
7083
|
+
return null;
|
|
7084
|
+
}
|
|
7085
|
+
}
|
|
7086
|
+
async function processToolCalls(response, workingDir) {
|
|
7087
|
+
const outputs = [];
|
|
7088
|
+
const toolRegex = /<tool\s+name="([^"]+)">\n?([\s\S]*?)<\/tool>/g;
|
|
7089
|
+
let match;
|
|
7090
|
+
while ((match = toolRegex.exec(response)) !== null) {
|
|
7091
|
+
const toolName = match[1];
|
|
7092
|
+
const argsBlock = match[2].trim();
|
|
7093
|
+
const args = {};
|
|
7094
|
+
for (const line of argsBlock.split("\n")) {
|
|
7095
|
+
const eqIdx = line.indexOf("=");
|
|
7096
|
+
if (eqIdx > 0) {
|
|
7097
|
+
const key = line.slice(0, eqIdx).trim();
|
|
7098
|
+
const value = line.slice(eqIdx + 1).trim();
|
|
7099
|
+
args[key] = value;
|
|
7100
|
+
}
|
|
7101
|
+
}
|
|
7102
|
+
try {
|
|
7103
|
+
const tool = await getTool2(toolName);
|
|
7104
|
+
if (!tool) {
|
|
7105
|
+
outputs.push(`Unknown tool: ${toolName}`);
|
|
7106
|
+
continue;
|
|
7107
|
+
}
|
|
7108
|
+
const result = await tool.execute(workingDir, args);
|
|
7109
|
+
const output = result.success ? `[${toolName}] result:
|
|
7110
|
+
${result.output.slice(0, 3e3)}` : `[${toolName}] error: ${result.error || result.output}`;
|
|
7111
|
+
outputs.push(output);
|
|
7112
|
+
} catch (err) {
|
|
7113
|
+
outputs.push(`[${toolName}] exception: ${err.message}`);
|
|
7114
|
+
}
|
|
7115
|
+
}
|
|
7116
|
+
return outputs;
|
|
7117
|
+
}
|
|
7118
|
+
async function getTool2(name) {
|
|
7119
|
+
if (name === "read_file") {
|
|
7120
|
+
const { readFileSync: readFileSync18, existsSync: existsSync16 } = await import("fs");
|
|
7121
|
+
const { resolve: resolve5 } = await import("path");
|
|
7122
|
+
return {
|
|
7123
|
+
execute: async (workingDir, args) => {
|
|
7124
|
+
const filePath = resolve5(workingDir, args.path || ".");
|
|
7125
|
+
if (!existsSync16(filePath)) return { success: false, output: "", error: `File not found: ${args.path}` };
|
|
7126
|
+
const content = readFileSync18(filePath, "utf-8");
|
|
7127
|
+
const lines = content.split("\n");
|
|
7128
|
+
const numbered = lines.map((l, i) => `${String(i + 1).padStart(4, " ")} | ${l}`).join("\n");
|
|
7129
|
+
return { success: true, output: numbered };
|
|
7130
|
+
}
|
|
7131
|
+
};
|
|
7132
|
+
}
|
|
7133
|
+
if (name === "write_file") {
|
|
7134
|
+
const { writeFileSync: writeFileSync11, mkdirSync: mkdirSync10 } = await import("fs");
|
|
7135
|
+
const { resolve: resolve5, dirname: dirname6 } = await import("path");
|
|
7136
|
+
return {
|
|
7137
|
+
execute: async (workingDir, args) => {
|
|
7138
|
+
const filePath = resolve5(workingDir, args.path || "");
|
|
7139
|
+
mkdirSync10(dirname6(filePath), { recursive: true });
|
|
7140
|
+
writeFileSync11(filePath, args.content || "", "utf-8");
|
|
7141
|
+
return { success: true, output: `Wrote ${filePath}` };
|
|
7142
|
+
}
|
|
7143
|
+
};
|
|
7144
|
+
}
|
|
7145
|
+
if (name === "edit_file") {
|
|
7146
|
+
const { readFileSync: readFileSync18, writeFileSync: writeFileSync11, existsSync: existsSync16 } = await import("fs");
|
|
7147
|
+
const { resolve: resolve5 } = await import("path");
|
|
7148
|
+
return {
|
|
7149
|
+
execute: async (workingDir, args) => {
|
|
7150
|
+
const filePath = resolve5(workingDir, args.path || "");
|
|
7151
|
+
if (!existsSync16(filePath)) return { success: false, output: "", error: `File not found: ${args.path}` };
|
|
7152
|
+
const content = readFileSync18(filePath, "utf-8");
|
|
7153
|
+
if (!args.oldString) return { success: false, output: "", error: "oldString required" };
|
|
7154
|
+
const escaped = args.oldString.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
7155
|
+
const count = (content.match(new RegExp(escaped, "g")) || []).length;
|
|
7156
|
+
if (count === 0) return { success: false, output: "", error: `oldString not found in ${args.path}` };
|
|
7157
|
+
const updated = content.replaceAll(args.oldString, args.newString || "");
|
|
7158
|
+
writeFileSync11(filePath, updated, "utf-8");
|
|
7159
|
+
return { success: true, output: `Edited ${filePath} (${count} replacement${count > 1 ? "s" : ""})` };
|
|
7160
|
+
}
|
|
7161
|
+
};
|
|
7162
|
+
}
|
|
7163
|
+
if (name === "create_file") {
|
|
7164
|
+
const { writeFileSync: writeFileSync11, mkdirSync: mkdirSync10, existsSync: existsSync16 } = await import("fs");
|
|
7165
|
+
const { resolve: resolve5, dirname: dirname6 } = await import("path");
|
|
7166
|
+
return {
|
|
7167
|
+
execute: async (workingDir, args) => {
|
|
7168
|
+
const filePath = resolve5(workingDir, args.path || "");
|
|
7169
|
+
if (existsSync16(filePath)) return { success: false, output: "", error: `File exists: ${args.path}` };
|
|
7170
|
+
mkdirSync10(dirname6(filePath), { recursive: true });
|
|
7171
|
+
writeFileSync11(filePath, "", "utf-8");
|
|
7172
|
+
return { success: true, output: `Created ${filePath}` };
|
|
7173
|
+
}
|
|
7174
|
+
};
|
|
7175
|
+
}
|
|
7176
|
+
if (name === "delete_file") {
|
|
7177
|
+
const { unlinkSync: unlinkSync7, existsSync: existsSync16 } = await import("fs");
|
|
7178
|
+
const { resolve: resolve5 } = await import("path");
|
|
7179
|
+
return {
|
|
7180
|
+
execute: async (workingDir, args) => {
|
|
7181
|
+
const filePath = resolve5(workingDir, args.path || "");
|
|
7182
|
+
if (!existsSync16(filePath)) return { success: false, output: "", error: `File not found: ${args.path}` };
|
|
7183
|
+
unlinkSync7(filePath);
|
|
7184
|
+
return { success: true, output: `Deleted ${filePath}` };
|
|
7185
|
+
}
|
|
7186
|
+
};
|
|
7187
|
+
}
|
|
7188
|
+
if (name === "append_file") {
|
|
7189
|
+
const { appendFileSync: appendFileSync2, mkdirSync: mkdirSync10 } = await import("fs");
|
|
7190
|
+
const { resolve: resolve5, dirname: dirname6 } = await import("path");
|
|
7191
|
+
return {
|
|
7192
|
+
execute: async (workingDir, args) => {
|
|
7193
|
+
const filePath = resolve5(workingDir, args.path || "");
|
|
7194
|
+
mkdirSync10(dirname6(filePath), { recursive: true });
|
|
7195
|
+
appendFileSync2(filePath, (args.content || "") + "\n", "utf-8");
|
|
7196
|
+
return { success: true, output: `Appended to ${filePath}` };
|
|
7197
|
+
}
|
|
7198
|
+
};
|
|
7199
|
+
}
|
|
7200
|
+
if (name === "grep_search") {
|
|
7201
|
+
const { execSync: execSync5 } = await import("child_process");
|
|
7202
|
+
return {
|
|
7203
|
+
execute: async (workingDir, args) => {
|
|
7204
|
+
try {
|
|
7205
|
+
const pattern = args.pattern || "";
|
|
7206
|
+
const cmd = `rg -n '${pattern.replace(/'/g, "'\\''")}' 2>/dev/null || echo "(no matches)"`;
|
|
7207
|
+
const output = execSync5(cmd, { cwd: workingDir, encoding: "utf-8", maxBuffer: 1024 * 1024 });
|
|
7208
|
+
return { success: true, output: output.trim() };
|
|
7209
|
+
} catch {
|
|
7210
|
+
return { success: true, output: "(no matches)" };
|
|
7211
|
+
}
|
|
7212
|
+
}
|
|
7213
|
+
};
|
|
7214
|
+
}
|
|
7215
|
+
if (name === "glob_search") {
|
|
7216
|
+
const { execSync: execSync5 } = await import("child_process");
|
|
7217
|
+
return {
|
|
7218
|
+
execute: async (workingDir, args) => {
|
|
7219
|
+
try {
|
|
7220
|
+
const pattern = args.pattern || "*";
|
|
7221
|
+
const cmd = `ls -1 ${pattern} 2>/dev/null || echo "(no files found)"`;
|
|
7222
|
+
const output = execSync5(cmd, { cwd: workingDir, encoding: "utf-8", maxBuffer: 1024 * 1024 });
|
|
7223
|
+
return { success: true, output: output.trim() };
|
|
7224
|
+
} catch {
|
|
7225
|
+
return { success: true, output: "(no files found)" };
|
|
7226
|
+
}
|
|
7227
|
+
}
|
|
7228
|
+
};
|
|
7229
|
+
}
|
|
7230
|
+
if (name === "read_dir") {
|
|
7231
|
+
const { readdirSync: readdirSync13, existsSync: existsSync16 } = await import("fs");
|
|
7232
|
+
const { resolve: resolve5 } = await import("path");
|
|
7233
|
+
return {
|
|
7234
|
+
execute: async (workingDir, args) => {
|
|
7235
|
+
const dirPath = resolve5(workingDir, args.path || ".");
|
|
7236
|
+
if (!existsSync16(dirPath)) return { success: false, output: "", error: `Directory not found: ${args.path}` };
|
|
7237
|
+
const entries = readdirSync13(dirPath, { withFileTypes: true });
|
|
7238
|
+
const output = entries.map((e) => e.isDirectory() ? `${e.name}/` : e.name).join("\n");
|
|
7239
|
+
return { success: true, output };
|
|
7240
|
+
}
|
|
7241
|
+
};
|
|
7242
|
+
}
|
|
7243
|
+
if (name === "execute_command") {
|
|
7244
|
+
const { execSync: execSync5 } = await import("child_process");
|
|
7245
|
+
return {
|
|
7246
|
+
execute: async (workingDir, args) => {
|
|
7247
|
+
try {
|
|
7248
|
+
const command = args.command || "";
|
|
7249
|
+
const timeout = parseInt(args.timeout || "30000", 10);
|
|
7250
|
+
const output = execSync5(command, {
|
|
7251
|
+
cwd: workingDir,
|
|
7252
|
+
encoding: "utf-8",
|
|
7253
|
+
maxBuffer: 1024 * 1024,
|
|
7254
|
+
timeout
|
|
7255
|
+
});
|
|
7256
|
+
return { success: true, output: output.trim() };
|
|
7257
|
+
} catch (err) {
|
|
7258
|
+
const e = err;
|
|
7259
|
+
return { success: false, output: e.stderr || e.message, error: e.message };
|
|
7260
|
+
}
|
|
7261
|
+
}
|
|
7262
|
+
};
|
|
7263
|
+
}
|
|
7264
|
+
return null;
|
|
7265
|
+
}
|
|
6891
7266
|
async function cmdTUI(options) {
|
|
6892
|
-
|
|
7267
|
+
const workingDir = options.dir || process.cwd();
|
|
7268
|
+
console.log(chalk36.dim("Starting LoveCode TUI..."));
|
|
6893
7269
|
if (options.theme) {
|
|
6894
7270
|
const mod = await import("./theme-ZRZYRB2Q.js");
|
|
6895
7271
|
const names = mod.getThemeNames();
|
|
6896
|
-
|
|
6897
|
-
|
|
6898
|
-
|
|
6899
|
-
|
|
6900
|
-
|
|
6901
|
-
|
|
6902
|
-
|
|
7272
|
+
if (names.includes(options.theme)) {
|
|
7273
|
+
mod.setTheme(options.theme);
|
|
7274
|
+
}
|
|
7275
|
+
}
|
|
7276
|
+
const ai = await loadAIProvider();
|
|
7277
|
+
if (!ai) {
|
|
7278
|
+
startTUI({
|
|
7279
|
+
projectName: "LoveCode AI",
|
|
7280
|
+
branch: "main",
|
|
7281
|
+
fileCount: 142,
|
|
7282
|
+
language: "TypeScript",
|
|
7283
|
+
framework: "Node.js",
|
|
7284
|
+
repoStatus: "clean",
|
|
7285
|
+
messages: [],
|
|
7286
|
+
onSendMessage: async () => "No AI provider configured. Run `lovecode init` to set one up.",
|
|
7287
|
+
onRunCommand: async () => "No AI provider configured."
|
|
7288
|
+
});
|
|
7289
|
+
return;
|
|
6903
7290
|
}
|
|
7291
|
+
const { provider, config } = ai;
|
|
7292
|
+
const messages = [{ role: "system", content: TOOL_SYSTEM_PROMPT }];
|
|
6904
7293
|
startTUI({
|
|
6905
7294
|
projectName: "LoveCode AI",
|
|
6906
7295
|
branch: "main",
|
|
@@ -6909,13 +7298,32 @@ async function cmdTUI(options) {
|
|
|
6909
7298
|
framework: "Node.js",
|
|
6910
7299
|
repoStatus: "clean",
|
|
6911
7300
|
messages: [],
|
|
7301
|
+
provider: provider.name,
|
|
7302
|
+
model: config.model,
|
|
6912
7303
|
onSendMessage: async (msg) => {
|
|
6913
|
-
|
|
6914
|
-
|
|
6915
|
-
|
|
7304
|
+
messages.push({ role: "user", content: msg });
|
|
7305
|
+
for (let round = 0; round < 5; round++) {
|
|
7306
|
+
const response = await provider.chat(messages, config);
|
|
7307
|
+
messages.push({ role: "assistant", content: response });
|
|
7308
|
+
const toolOutputs = await processToolCalls(response, workingDir);
|
|
7309
|
+
if (toolOutputs.length === 0) {
|
|
7310
|
+
return response;
|
|
7311
|
+
}
|
|
7312
|
+
for (const output of toolOutputs) {
|
|
7313
|
+
messages.push({ role: "user", content: output });
|
|
7314
|
+
}
|
|
7315
|
+
}
|
|
7316
|
+
return "Max tool call rounds reached. Please try again with a simpler request.";
|
|
6916
7317
|
},
|
|
6917
|
-
onRunCommand: async (
|
|
6918
|
-
|
|
7318
|
+
onRunCommand: async (command) => {
|
|
7319
|
+
const { execSync: execSync5 } = await import("child_process");
|
|
7320
|
+
try {
|
|
7321
|
+
const output = execSync5(command, { cwd: workingDir, encoding: "utf-8", timeout: 6e4 });
|
|
7322
|
+
return output.trim();
|
|
7323
|
+
} catch (err) {
|
|
7324
|
+
const e = err;
|
|
7325
|
+
return e.stderr || e.message;
|
|
7326
|
+
}
|
|
6919
7327
|
}
|
|
6920
7328
|
});
|
|
6921
7329
|
}
|
|
@@ -6925,20 +7333,23 @@ var tuiCommand = new Command13("tui").alias("ui").description("Launch the Termin
|
|
|
6925
7333
|
Escape Enter vim normal mode
|
|
6926
7334
|
i Enter vim insert mode
|
|
6927
7335
|
j/k Scroll up/down (vim normal mode)
|
|
6928
|
-
Ctrl+N/P Next/previous pane
|
|
6929
7336
|
|
|
6930
7337
|
Slash commands:
|
|
6931
7338
|
/help Show help
|
|
6932
7339
|
/clear Clear messages
|
|
6933
|
-
/theme <n> Change theme
|
|
6934
|
-
/
|
|
7340
|
+
/theme <n> Change theme
|
|
7341
|
+
/connect Show providers
|
|
7342
|
+
/model Show AI config
|
|
7343
|
+
/export Save chat to file
|
|
6935
7344
|
/!<cmd> Run a shell command
|
|
6936
7345
|
/exit Quit TUI
|
|
7346
|
+
|
|
7347
|
+
The AI can read, write, and edit files in the project directory.
|
|
6937
7348
|
`);
|
|
6938
7349
|
|
|
6939
7350
|
// src/commands/plugin.ts
|
|
6940
7351
|
import { Command as Command14 } from "commander";
|
|
6941
|
-
import
|
|
7352
|
+
import chalk37 from "chalk";
|
|
6942
7353
|
async function cmdListPlugins() {
|
|
6943
7354
|
const { listPlugins: listPlugins2, formatPluginList } = await import("./registry-MW5ISDO7.js");
|
|
6944
7355
|
const plugins = listPlugins2();
|
|
@@ -6947,12 +7358,12 @@ async function cmdListPlugins() {
|
|
|
6947
7358
|
async function cmdEnablePlugin(name) {
|
|
6948
7359
|
const { enablePlugin: enablePlugin2 } = await import("./registry-MW5ISDO7.js");
|
|
6949
7360
|
const ok = enablePlugin2(name);
|
|
6950
|
-
console.log(ok ?
|
|
7361
|
+
console.log(ok ? chalk37.green(`Enabled plugin: ${name}`) : chalk37.red(`Plugin not found: ${name}`));
|
|
6951
7362
|
}
|
|
6952
7363
|
async function cmdDisablePlugin(name) {
|
|
6953
7364
|
const { disablePlugin: disablePlugin2 } = await import("./registry-MW5ISDO7.js");
|
|
6954
7365
|
const ok = disablePlugin2(name);
|
|
6955
|
-
console.log(ok ?
|
|
7366
|
+
console.log(ok ? chalk37.yellow(`Disabled plugin: ${name}`) : chalk37.red(`Plugin not found: ${name}`));
|
|
6956
7367
|
}
|
|
6957
7368
|
async function cmdSearchMarketplace(query) {
|
|
6958
7369
|
const { searchMarketplace, formatMarketplace } = await import("./registry-MW5ISDO7.js");
|
|
@@ -6973,37 +7384,37 @@ pluginCommand.command("search").description("Search plugin marketplace").argumen
|
|
|
6973
7384
|
|
|
6974
7385
|
// src/commands/browser.ts
|
|
6975
7386
|
import { Command as Command15 } from "commander";
|
|
6976
|
-
import
|
|
7387
|
+
import chalk38 from "chalk";
|
|
6977
7388
|
async function cmdStart(options) {
|
|
6978
7389
|
const { launchBrowser } = await import("./playwright-N7OAVW2N.js");
|
|
6979
7390
|
try {
|
|
6980
7391
|
await launchBrowser({ headless: options.headless !== false });
|
|
6981
|
-
console.log(
|
|
7392
|
+
console.log(chalk38.green("Browser launched."));
|
|
6982
7393
|
} catch (err) {
|
|
6983
|
-
console.log(
|
|
7394
|
+
console.log(chalk38.red(`Failed: ${err.message}`));
|
|
6984
7395
|
}
|
|
6985
7396
|
}
|
|
6986
7397
|
async function cmdStop() {
|
|
6987
7398
|
const { closeBrowser } = await import("./playwright-N7OAVW2N.js");
|
|
6988
7399
|
await closeBrowser();
|
|
6989
|
-
console.log(
|
|
7400
|
+
console.log(chalk38.yellow("Browser closed."));
|
|
6990
7401
|
}
|
|
6991
7402
|
async function cmdGoto(url) {
|
|
6992
7403
|
const { goto: goto2 } = await import("./playwright-N7OAVW2N.js");
|
|
6993
7404
|
try {
|
|
6994
7405
|
const result = await goto2(url);
|
|
6995
|
-
console.log(
|
|
7406
|
+
console.log(chalk38.green(result));
|
|
6996
7407
|
} catch (err) {
|
|
6997
|
-
console.log(
|
|
7408
|
+
console.log(chalk38.red(`Error: ${err.message}`));
|
|
6998
7409
|
}
|
|
6999
7410
|
}
|
|
7000
7411
|
async function cmdClick(selector) {
|
|
7001
7412
|
const { click: click2 } = await import("./playwright-N7OAVW2N.js");
|
|
7002
7413
|
try {
|
|
7003
7414
|
const result = await click2(selector);
|
|
7004
|
-
console.log(
|
|
7415
|
+
console.log(chalk38.green(result));
|
|
7005
7416
|
} catch (err) {
|
|
7006
|
-
console.log(
|
|
7417
|
+
console.log(chalk38.red(`Error: ${err.message}`));
|
|
7007
7418
|
}
|
|
7008
7419
|
}
|
|
7009
7420
|
async function cmdType(selector, text, options) {
|
|
@@ -7011,18 +7422,18 @@ async function cmdType(selector, text, options) {
|
|
|
7011
7422
|
const value = text || options.text || "";
|
|
7012
7423
|
try {
|
|
7013
7424
|
const result = await type2(selector, value);
|
|
7014
|
-
console.log(
|
|
7425
|
+
console.log(chalk38.green(result));
|
|
7015
7426
|
} catch (err) {
|
|
7016
|
-
console.log(
|
|
7427
|
+
console.log(chalk38.red(`Error: ${err.message}`));
|
|
7017
7428
|
}
|
|
7018
7429
|
}
|
|
7019
7430
|
async function cmdScreenshot(options) {
|
|
7020
7431
|
const { screenshot: screenshot2, formatScreenshotResult: formatScreenshotResult2 } = await import("./playwright-N7OAVW2N.js");
|
|
7021
7432
|
try {
|
|
7022
7433
|
const result = await screenshot2(options.name);
|
|
7023
|
-
console.log(
|
|
7434
|
+
console.log(chalk38.green(formatScreenshotResult2(result)));
|
|
7024
7435
|
} catch (err) {
|
|
7025
|
-
console.log(
|
|
7436
|
+
console.log(chalk38.red(`Error: ${err.message}`));
|
|
7026
7437
|
}
|
|
7027
7438
|
}
|
|
7028
7439
|
async function cmdInspect(selector) {
|
|
@@ -7032,10 +7443,10 @@ async function cmdInspect(selector) {
|
|
|
7032
7443
|
if (el) {
|
|
7033
7444
|
console.log(formatDOMElement2(el));
|
|
7034
7445
|
} else {
|
|
7035
|
-
console.log(
|
|
7446
|
+
console.log(chalk38.yellow(`Element not found: ${selector}`));
|
|
7036
7447
|
}
|
|
7037
7448
|
} catch (err) {
|
|
7038
|
-
console.log(
|
|
7449
|
+
console.log(chalk38.red(`Error: ${err.message}`));
|
|
7039
7450
|
}
|
|
7040
7451
|
}
|
|
7041
7452
|
async function cmdActions(actions, options) {
|
|
@@ -7051,10 +7462,10 @@ async function cmdActions(actions, options) {
|
|
|
7051
7462
|
try {
|
|
7052
7463
|
const results = await runActions(parsedActions);
|
|
7053
7464
|
for (const r of results) {
|
|
7054
|
-
console.log(
|
|
7465
|
+
console.log(chalk38.cyan(` \u2192 ${r.slice(0, 200)}`));
|
|
7055
7466
|
}
|
|
7056
7467
|
} catch (err) {
|
|
7057
|
-
console.log(
|
|
7468
|
+
console.log(chalk38.red(`Error: ${err.message}`));
|
|
7058
7469
|
}
|
|
7059
7470
|
}
|
|
7060
7471
|
var browserCommand = new Command15("browser").alias("br").description("Browser automation \u2014 Playwright-powered").addHelpText("after", `
|
|
@@ -7081,10 +7492,10 @@ browserCommand.command("actions").description("Run a sequence of browser actions
|
|
|
7081
7492
|
|
|
7082
7493
|
// src/commands/security.ts
|
|
7083
7494
|
import { Command as Command16 } from "commander";
|
|
7084
|
-
import
|
|
7495
|
+
import chalk43 from "chalk";
|
|
7085
7496
|
|
|
7086
7497
|
// src/security/risk.ts
|
|
7087
|
-
import
|
|
7498
|
+
import chalk39 from "chalk";
|
|
7088
7499
|
var COMMAND_RULES = [
|
|
7089
7500
|
{ pattern: /^rm\s+-rf\s+(\/|\/\w+|\.)/, score: 100, reason: "Destructive recursive delete" },
|
|
7090
7501
|
{ pattern: /^rm\s+-rf/, score: 80, reason: "Recursive force delete" },
|
|
@@ -7195,21 +7606,21 @@ function scoreToAction(score) {
|
|
|
7195
7606
|
}
|
|
7196
7607
|
function formatRisk(risk) {
|
|
7197
7608
|
const colors = {
|
|
7198
|
-
safe:
|
|
7199
|
-
low:
|
|
7200
|
-
medium:
|
|
7201
|
-
high:
|
|
7202
|
-
critical:
|
|
7609
|
+
safe: chalk39.green,
|
|
7610
|
+
low: chalk39.cyan,
|
|
7611
|
+
medium: chalk39.yellow,
|
|
7612
|
+
high: chalk39.red,
|
|
7613
|
+
critical: chalk39.bgRed.white
|
|
7203
7614
|
};
|
|
7204
|
-
const color = colors[risk.level] ||
|
|
7615
|
+
const color = colors[risk.level] || chalk39.dim;
|
|
7205
7616
|
const label = risk.level.toUpperCase().padEnd(10);
|
|
7206
|
-
const action = risk.suggestedAction === "auto" ?
|
|
7207
|
-
return ` ${color(label)} score=${risk.score} action=${action} ${
|
|
7617
|
+
const action = risk.suggestedAction === "auto" ? chalk39.green(risk.suggestedAction) : risk.suggestedAction === "confirm" ? chalk39.yellow(risk.suggestedAction) : chalk39.bgRed.white(` ${risk.suggestedAction} `);
|
|
7618
|
+
return ` ${color(label)} score=${risk.score} action=${action} ${chalk39.dim(risk.reasons.join(", "))}`;
|
|
7208
7619
|
}
|
|
7209
7620
|
|
|
7210
7621
|
// src/security/secrets.ts
|
|
7211
7622
|
import * as fs21 from "fs";
|
|
7212
|
-
import
|
|
7623
|
+
import chalk40 from "chalk";
|
|
7213
7624
|
var SECRET_RULES = [
|
|
7214
7625
|
{ type: "aws_key", pattern: /(?:AKIA|ASIA)[0-9A-Z]{16}/g, severity: "critical", description: "AWS Access Key ID" },
|
|
7215
7626
|
{ type: "gcp_key", pattern: /AIza[0-9A-Za-z\-_]{35}/g, severity: "critical", description: "GCP API Key" },
|
|
@@ -7294,30 +7705,30 @@ function scanDirectory2(dirPath, maxFiles = 100) {
|
|
|
7294
7705
|
}
|
|
7295
7706
|
function formatSecretMatch(match) {
|
|
7296
7707
|
const severityColor = {
|
|
7297
|
-
low:
|
|
7298
|
-
medium:
|
|
7299
|
-
high:
|
|
7300
|
-
critical:
|
|
7708
|
+
low: chalk40.cyan,
|
|
7709
|
+
medium: chalk40.yellow,
|
|
7710
|
+
high: chalk40.red,
|
|
7711
|
+
critical: chalk40.bgRed.white
|
|
7301
7712
|
};
|
|
7302
|
-
const sev = severityColor[match.severity] ||
|
|
7303
|
-
const fileInfo = match.file ? `${
|
|
7713
|
+
const sev = severityColor[match.severity] || chalk40.dim;
|
|
7714
|
+
const fileInfo = match.file ? `${chalk40.dim(match.file)}:${match.line}:${match.column}` : `line ${match.line}:${match.column}`;
|
|
7304
7715
|
return ` ${sev(match.severity.toUpperCase().padEnd(10))} ${match.type.padEnd(20)} ${match.value.padEnd(30)} ${fileInfo}
|
|
7305
|
-
${
|
|
7716
|
+
${chalk40.dim(match.context)}`;
|
|
7306
7717
|
}
|
|
7307
7718
|
function formatSecretSummary(matches) {
|
|
7308
|
-
if (matches.length === 0) return
|
|
7719
|
+
if (matches.length === 0) return chalk40.green("No secrets detected.");
|
|
7309
7720
|
const bySeverity = /* @__PURE__ */ new Map();
|
|
7310
7721
|
for (const m of matches) {
|
|
7311
7722
|
const arr = bySeverity.get(m.severity) || [];
|
|
7312
7723
|
arr.push(m);
|
|
7313
7724
|
bySeverity.set(m.severity, arr);
|
|
7314
7725
|
}
|
|
7315
|
-
const lines = [
|
|
7726
|
+
const lines = [chalk40.bold(`
|
|
7316
7727
|
Secrets Detected (${matches.length}):`)];
|
|
7317
7728
|
for (const [severity, ms] of bySeverity) {
|
|
7318
7729
|
const label = severity.toUpperCase();
|
|
7319
7730
|
lines.push(`
|
|
7320
|
-
${
|
|
7731
|
+
${chalk40.bold(label)} (${ms.length}):`);
|
|
7321
7732
|
for (const m of ms.slice(0, 10)) {
|
|
7322
7733
|
lines.push(formatSecretMatch(m));
|
|
7323
7734
|
}
|
|
@@ -7327,7 +7738,7 @@ function formatSecretSummary(matches) {
|
|
|
7327
7738
|
}
|
|
7328
7739
|
|
|
7329
7740
|
// src/security/sandbox.ts
|
|
7330
|
-
import
|
|
7741
|
+
import chalk41 from "chalk";
|
|
7331
7742
|
var PROFILES = {
|
|
7332
7743
|
isolated: {
|
|
7333
7744
|
name: "isolated",
|
|
@@ -7456,10 +7867,10 @@ function checkCommand(command, cwd, profileName) {
|
|
|
7456
7867
|
return { allowed: true };
|
|
7457
7868
|
}
|
|
7458
7869
|
function formatProfile(profile) {
|
|
7459
|
-
const yes =
|
|
7460
|
-
const no =
|
|
7870
|
+
const yes = chalk41.green("\u2713");
|
|
7871
|
+
const no = chalk41.red("\u2717");
|
|
7461
7872
|
return [
|
|
7462
|
-
`${
|
|
7873
|
+
`${chalk41.bold(profile.name.toUpperCase())}`,
|
|
7463
7874
|
` Network: ${profile.allowNetwork ? yes : no}`,
|
|
7464
7875
|
` File Read: ${profile.allowFileRead ? yes : no}`,
|
|
7465
7876
|
` File Write: ${profile.allowFileWrite ? yes : no}`,
|
|
@@ -7473,7 +7884,7 @@ function formatProfile(profile) {
|
|
|
7473
7884
|
// src/security/permissions.ts
|
|
7474
7885
|
import * as fs22 from "fs";
|
|
7475
7886
|
import * as path20 from "path";
|
|
7476
|
-
import
|
|
7887
|
+
import chalk42 from "chalk";
|
|
7477
7888
|
var PERMISSION_FILE = ".lovecode/permissions.json";
|
|
7478
7889
|
var DEFAULT_PERMISSIONS = {
|
|
7479
7890
|
version: 1,
|
|
@@ -7586,24 +7997,24 @@ function resetPermissions(rootDir) {
|
|
|
7586
7997
|
cachedPermissions = null;
|
|
7587
7998
|
}
|
|
7588
7999
|
function formatPermissions(perms) {
|
|
7589
|
-
const lines = [
|
|
7590
|
-
lines.push(` ${
|
|
7591
|
-
lines.push(` ${
|
|
7592
|
-
lines.push(` ${
|
|
7593
|
-
lines.push(` ${
|
|
7594
|
-
lines.push(` ${
|
|
8000
|
+
const lines = [chalk42.bold("\n Permission Settings")];
|
|
8001
|
+
lines.push(` ${chalk42.dim("File Read:")} ${formatAction(perms.defaults.fileRead)}`);
|
|
8002
|
+
lines.push(` ${chalk42.dim("File Write:")} ${formatAction(perms.defaults.fileWrite)}`);
|
|
8003
|
+
lines.push(` ${chalk42.dim("Network:")} ${formatAction(perms.defaults.networkAccess)}`);
|
|
8004
|
+
lines.push(` ${chalk42.dim("Commands:")} ${formatAction(perms.defaults.commandExecution)}`);
|
|
8005
|
+
lines.push(` ${chalk42.dim("Environment:")} ${formatAction(perms.defaults.environmentAccess)}`);
|
|
7595
8006
|
if (perms.entries.length > 0) {
|
|
7596
8007
|
lines.push(`
|
|
7597
|
-
${
|
|
8008
|
+
${chalk42.bold("Specific Permissions:")}`);
|
|
7598
8009
|
for (const e of perms.entries) {
|
|
7599
|
-
lines.push(` ${formatAction(e.action)} ${e.resource}${e.reason ? ` ${
|
|
8010
|
+
lines.push(` ${formatAction(e.action)} ${e.resource}${e.reason ? ` ${chalk42.dim(`(${e.reason})`)}` : ""}`);
|
|
7600
8011
|
}
|
|
7601
8012
|
}
|
|
7602
8013
|
if (perms.trustedSources.length > 0) {
|
|
7603
8014
|
lines.push(`
|
|
7604
|
-
${
|
|
8015
|
+
${chalk42.bold("Trusted Sources:")}`);
|
|
7605
8016
|
for (const s of perms.trustedSources) {
|
|
7606
|
-
lines.push(` ${
|
|
8017
|
+
lines.push(` ${chalk42.green("\u2713")} ${s}`);
|
|
7607
8018
|
}
|
|
7608
8019
|
}
|
|
7609
8020
|
return lines.join("\n");
|
|
@@ -7611,11 +8022,11 @@ function formatPermissions(perms) {
|
|
|
7611
8022
|
function formatAction(action) {
|
|
7612
8023
|
switch (action) {
|
|
7613
8024
|
case "allow":
|
|
7614
|
-
return
|
|
8025
|
+
return chalk42.green("ALLOW");
|
|
7615
8026
|
case "deny":
|
|
7616
|
-
return
|
|
8027
|
+
return chalk42.red("DENY");
|
|
7617
8028
|
case "ask":
|
|
7618
|
-
return
|
|
8029
|
+
return chalk42.yellow("ASK");
|
|
7619
8030
|
}
|
|
7620
8031
|
}
|
|
7621
8032
|
|
|
@@ -7624,8 +8035,8 @@ async function cmdAssessRisk(command, options) {
|
|
|
7624
8035
|
if (command) {
|
|
7625
8036
|
const risk = assessCommandRisk(command);
|
|
7626
8037
|
console.log(`
|
|
7627
|
-
${
|
|
7628
|
-
console.log(` ${
|
|
8038
|
+
${chalk43.bold("Command Risk Assessment")}`);
|
|
8039
|
+
console.log(` ${chalk43.dim(command)}`);
|
|
7629
8040
|
console.log(formatRisk(risk));
|
|
7630
8041
|
} else if (options.tool) {
|
|
7631
8042
|
let args;
|
|
@@ -7638,11 +8049,11 @@ async function cmdAssessRisk(command, options) {
|
|
|
7638
8049
|
}
|
|
7639
8050
|
const risk = assessToolRisk(options.tool, args);
|
|
7640
8051
|
console.log(`
|
|
7641
|
-
${
|
|
7642
|
-
console.log(` ${
|
|
8052
|
+
${chalk43.bold("Tool Risk Assessment")}`);
|
|
8053
|
+
console.log(` ${chalk43.dim(`${options.tool}${args ? " " + JSON.stringify(args) : ""}`)}`);
|
|
7643
8054
|
console.log(formatRisk(risk));
|
|
7644
8055
|
} else {
|
|
7645
|
-
console.log(
|
|
8056
|
+
console.log(chalk43.yellow("Provide a command string or --tool."));
|
|
7646
8057
|
}
|
|
7647
8058
|
}
|
|
7648
8059
|
async function cmdScanSecrets(options) {
|
|
@@ -7651,16 +8062,16 @@ async function cmdScanSecrets(options) {
|
|
|
7651
8062
|
console.log(formatSecretSummary(matches));
|
|
7652
8063
|
} else if (options.dir) {
|
|
7653
8064
|
const max = parseInt(options.max || "100", 10);
|
|
7654
|
-
console.log(
|
|
8065
|
+
console.log(chalk43.dim(`Scanning ${options.dir} for secrets...`));
|
|
7655
8066
|
const matches = scanDirectory2(options.dir, max);
|
|
7656
8067
|
console.log(formatSecretSummary(matches));
|
|
7657
8068
|
} else {
|
|
7658
|
-
console.log(
|
|
8069
|
+
console.log(chalk43.yellow("Provide --text or --dir."));
|
|
7659
8070
|
}
|
|
7660
8071
|
}
|
|
7661
8072
|
async function cmdProfiles() {
|
|
7662
8073
|
const profiles = listProfiles();
|
|
7663
|
-
console.log(
|
|
8074
|
+
console.log(chalk43.bold("\n Sandbox Profiles:\n"));
|
|
7664
8075
|
for (const p of profiles) {
|
|
7665
8076
|
console.log(formatProfile(p));
|
|
7666
8077
|
console.log("");
|
|
@@ -7669,35 +8080,35 @@ async function cmdProfiles() {
|
|
|
7669
8080
|
async function cmdSandbox(command, options) {
|
|
7670
8081
|
const result = checkCommand(command, process.cwd(), options.profile);
|
|
7671
8082
|
if (result.allowed) {
|
|
7672
|
-
console.log(
|
|
7673
|
-
Allowed: ${
|
|
8083
|
+
console.log(chalk43.green(`
|
|
8084
|
+
Allowed: ${chalk43.dim(command)}`));
|
|
7674
8085
|
} else {
|
|
7675
|
-
console.log(
|
|
7676
|
-
Blocked: ${
|
|
7677
|
-
console.log(` ${
|
|
8086
|
+
console.log(chalk43.red(`
|
|
8087
|
+
Blocked: ${chalk43.dim(command)}`));
|
|
8088
|
+
console.log(` ${chalk43.yellow(result.reason)}`);
|
|
7678
8089
|
}
|
|
7679
8090
|
}
|
|
7680
8091
|
async function cmdPermissions(options) {
|
|
7681
8092
|
if (options.set && options.action) {
|
|
7682
8093
|
const defaults = ["fileRead", "fileWrite", "networkAccess", "commandExecution", "environmentAccess"];
|
|
7683
8094
|
if (!defaults.includes(options.set)) {
|
|
7684
|
-
console.log(
|
|
8095
|
+
console.log(chalk43.red(`Invalid default: ${options.set}. Options: ${defaults.join(", ")}`));
|
|
7685
8096
|
return;
|
|
7686
8097
|
}
|
|
7687
8098
|
const perms = setDefault(options.set, options.action, options.dir);
|
|
7688
|
-
console.log(
|
|
8099
|
+
console.log(chalk43.green(`Default "${options.set}" set to "${options.action}".`));
|
|
7689
8100
|
console.log(formatPermissions(perms));
|
|
7690
8101
|
} else if (options.add && options.action) {
|
|
7691
8102
|
const action = options.action;
|
|
7692
8103
|
const perms = addPermission(options.add, action, void 0, options.dir);
|
|
7693
|
-
console.log(
|
|
8104
|
+
console.log(chalk43.green(`Permission added: ${options.add} \u2192 ${action}`));
|
|
7694
8105
|
console.log(formatPermissions(perms));
|
|
7695
8106
|
} else if (options.remove) {
|
|
7696
8107
|
const ok = removePermission(options.remove, options.dir);
|
|
7697
|
-
console.log(ok ?
|
|
8108
|
+
console.log(ok ? chalk43.green(`Removed permission: ${options.remove}`) : chalk43.yellow(`Permission not found: ${options.remove}`));
|
|
7698
8109
|
} else if (options.source) {
|
|
7699
8110
|
addTrustedSource(options.source, options.dir);
|
|
7700
|
-
console.log(
|
|
8111
|
+
console.log(chalk43.green(`Trusted source added: ${options.source}`));
|
|
7701
8112
|
} else {
|
|
7702
8113
|
const perms = loadPermissions(options.dir);
|
|
7703
8114
|
console.log(formatPermissions(perms));
|
|
@@ -7706,16 +8117,16 @@ async function cmdPermissions(options) {
|
|
|
7706
8117
|
async function cmdCheckPermission(resource, options) {
|
|
7707
8118
|
const category = options.category || "fileRead";
|
|
7708
8119
|
const result = checkPermission(resource, category, options.dir);
|
|
7709
|
-
const color = result === "allow" ?
|
|
7710
|
-
console.log(` ${color(result.toUpperCase())} ${
|
|
8120
|
+
const color = result === "allow" ? chalk43.green : result === "deny" ? chalk43.red : chalk43.yellow;
|
|
8121
|
+
console.log(` ${color(result.toUpperCase())} ${chalk43.dim(resource)} (${category})`);
|
|
7711
8122
|
}
|
|
7712
8123
|
async function cmdReset(options) {
|
|
7713
8124
|
resetPermissions(options.dir);
|
|
7714
|
-
console.log(
|
|
8125
|
+
console.log(chalk43.green("Permissions reset to defaults."));
|
|
7715
8126
|
}
|
|
7716
8127
|
async function cmdRemoveSource(source, options) {
|
|
7717
8128
|
const ok = removeTrustedSource(source, options.dir);
|
|
7718
|
-
console.log(ok ?
|
|
8129
|
+
console.log(ok ? chalk43.green(`Removed trusted source: ${source}`) : chalk43.yellow(`Source not found: ${source}`));
|
|
7719
8130
|
}
|
|
7720
8131
|
var securityCommand = new Command16("security").alias("sec").alias("secure").description("Security tools: risk assessment, secret detection, sandbox, permissions").addHelpText("after", `
|
|
7721
8132
|
Examples:
|
|
@@ -7750,7 +8161,7 @@ var pkg = {
|
|
|
7750
8161
|
description: "Terminal-native autonomous coding agent powered by free AI models"
|
|
7751
8162
|
};
|
|
7752
8163
|
var program = new Command17();
|
|
7753
|
-
program.name(pkg.name).description(
|
|
8164
|
+
program.name(pkg.name).description(chalk44.cyan(pkg.description)).version(pkg.version, "-v, --version", "Output the current version");
|
|
7754
8165
|
program.addCommand(chatCommand);
|
|
7755
8166
|
program.addCommand(runCommand);
|
|
7756
8167
|
program.addCommand(initCommand);
|