@contextos/cli 0.1.0 → 0.2.2
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 +648 -21
- package/package.json +40 -40
package/dist/index.js
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
// src/index.ts
|
|
4
4
|
import { Command as Command14 } from "commander";
|
|
5
|
+
import { join as join7 } from "path";
|
|
6
|
+
import { existsSync as existsSync4, mkdirSync as mkdirSync3, appendFileSync } from "fs";
|
|
5
7
|
|
|
6
8
|
// src/commands/init.ts
|
|
7
9
|
import { Command } from "commander";
|
|
@@ -835,18 +837,36 @@ function parseValue(value) {
|
|
|
835
837
|
import { Command as Command10 } from "commander";
|
|
836
838
|
import chalk10 from "chalk";
|
|
837
839
|
import ora9 from "ora";
|
|
838
|
-
import {
|
|
839
|
-
import {
|
|
840
|
+
import { readdirSync } from "fs";
|
|
841
|
+
import { readFile } from "fs/promises";
|
|
842
|
+
import { join as join4, relative, resolve, normalize } from "path";
|
|
840
843
|
import {
|
|
841
844
|
RLMEngine,
|
|
842
845
|
createGeminiClient as createGeminiClient3,
|
|
843
846
|
mergeFilesToContext
|
|
844
847
|
} from "@contextos/core";
|
|
845
|
-
function
|
|
848
|
+
function validatePath(userPath, projectRoot) {
|
|
849
|
+
const resolved = resolve(projectRoot, userPath);
|
|
850
|
+
const normalized = normalize(resolved);
|
|
851
|
+
const rootNormalized = normalize(projectRoot);
|
|
852
|
+
if (!normalized.startsWith(rootNormalized)) {
|
|
853
|
+
throw new Error(
|
|
854
|
+
`Invalid path: "${userPath}" escapes project boundaries.
|
|
855
|
+
Path must be within: ${rootNormalized}`
|
|
856
|
+
);
|
|
857
|
+
}
|
|
858
|
+
return normalized;
|
|
859
|
+
}
|
|
860
|
+
async function collectFiles(dir, extensions = [".ts", ".js", ".tsx", ".jsx", ".py", ".go", ".rs", ".java"], maxFiles = 100, maxDepth = 20) {
|
|
846
861
|
const files = [];
|
|
847
|
-
function walk(currentDir) {
|
|
862
|
+
async function walk(currentDir, depth) {
|
|
863
|
+
if (depth > maxDepth) {
|
|
864
|
+
console.warn(`Maximum depth (${maxDepth}) reached at ${currentDir}. Skipping deeper directories.`);
|
|
865
|
+
return;
|
|
866
|
+
}
|
|
848
867
|
if (files.length >= maxFiles) return;
|
|
849
868
|
const entries = readdirSync(currentDir, { withFileTypes: true });
|
|
869
|
+
const filePromises = [];
|
|
850
870
|
for (const entry of entries) {
|
|
851
871
|
if (files.length >= maxFiles) break;
|
|
852
872
|
const fullPath = join4(currentDir, entry.name);
|
|
@@ -854,32 +874,43 @@ function collectFiles(dir, extensions = [".ts", ".js", ".tsx", ".jsx", ".py", ".
|
|
|
854
874
|
if (["node_modules", ".git", "dist", "build", ".next", "__pycache__", "venv"].includes(entry.name)) {
|
|
855
875
|
continue;
|
|
856
876
|
}
|
|
857
|
-
walk(fullPath);
|
|
877
|
+
await walk(fullPath, depth + 1);
|
|
858
878
|
} else if (entry.isFile()) {
|
|
859
879
|
const ext = entry.name.substring(entry.name.lastIndexOf("."));
|
|
860
880
|
if (extensions.includes(ext)) {
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
881
|
+
const promise = (async () => {
|
|
882
|
+
try {
|
|
883
|
+
const content = await readFile(fullPath, "utf-8");
|
|
884
|
+
files.push({
|
|
885
|
+
path: relative(dir, fullPath),
|
|
886
|
+
content
|
|
887
|
+
});
|
|
888
|
+
} catch {
|
|
889
|
+
}
|
|
890
|
+
})();
|
|
891
|
+
filePromises.push(promise);
|
|
892
|
+
if (filePromises.length >= 10) {
|
|
893
|
+
await Promise.all(filePromises);
|
|
894
|
+
filePromises.length = 0;
|
|
868
895
|
}
|
|
869
896
|
}
|
|
870
897
|
}
|
|
871
898
|
}
|
|
899
|
+
if (filePromises.length > 0) {
|
|
900
|
+
await Promise.all(filePromises);
|
|
901
|
+
}
|
|
872
902
|
}
|
|
873
|
-
walk(dir);
|
|
903
|
+
await walk(dir, 0);
|
|
874
904
|
return files;
|
|
875
905
|
}
|
|
876
|
-
var analyzeCommand = new Command10("analyze").description("RLM-powered deep analysis of your codebase").argument("<goal>", 'Analysis goal or question (e.g., "Find all API endpoints")').option("-d, --depth <number>", "Maximum recursion depth for RLM", "3").option("-b, --budget <tokens>", "Token budget for analysis", "50000").option("-p, --path <path>", "Path to analyze (default: current directory)", ".").option("--max-files <number>", "Maximum files to include", "100").option("--verbose", "Show detailed execution trace").action(async (goal, options) => {
|
|
906
|
+
var analyzeCommand = new Command10("analyze").description("RLM-powered deep analysis of your codebase").argument("<goal>", 'Analysis goal or question (e.g., "Find all API endpoints")').option("-d, --depth <number>", "Maximum recursion depth for RLM", "3").option("-b, --budget <tokens>", "Token budget for analysis", "50000").option("-p, --path <path>", "Path to analyze (default: current directory)", ".").option("--max-files <number>", "Maximum files to include", "100").option("--max-depth <number>", "Maximum directory depth to traverse", "20").option("--verbose", "Show detailed execution trace").action(async (goal, options) => {
|
|
877
907
|
console.log(chalk10.blue.bold("\n\u{1F50D} RLM Analysis Engine\n"));
|
|
878
908
|
console.log(chalk10.gray("Goal: ") + chalk10.white(goal));
|
|
879
909
|
console.log();
|
|
880
910
|
const spinner = ora9("Collecting codebase files...").start();
|
|
881
911
|
try {
|
|
882
|
-
const
|
|
912
|
+
const safePath = validatePath(options.path, process.cwd());
|
|
913
|
+
const files = await collectFiles(safePath, void 0, parseInt(options.maxFiles), parseInt(options.maxDepth));
|
|
883
914
|
spinner.text = `Found ${files.length} files. Building context...`;
|
|
884
915
|
if (files.length === 0) {
|
|
885
916
|
spinner.fail("No source files found");
|
|
@@ -1079,7 +1110,16 @@ Respond in JSON format:
|
|
|
1079
1110
|
try {
|
|
1080
1111
|
const jsonMatch = result.answer.match(/\{[\s\S]*\}/);
|
|
1081
1112
|
if (jsonMatch) {
|
|
1082
|
-
|
|
1113
|
+
try {
|
|
1114
|
+
plan = JSON.parse(jsonMatch[0]);
|
|
1115
|
+
} catch (parseError) {
|
|
1116
|
+
spinner.warn("Failed to parse refactoring plan JSON");
|
|
1117
|
+
console.log();
|
|
1118
|
+
console.log(chalk11.blue.bold("Analysis Result:"));
|
|
1119
|
+
console.log(result.answer);
|
|
1120
|
+
console.log();
|
|
1121
|
+
process.exit(0);
|
|
1122
|
+
}
|
|
1083
1123
|
} else {
|
|
1084
1124
|
throw new Error("No JSON found in response");
|
|
1085
1125
|
}
|
|
@@ -1149,13 +1189,22 @@ Respond in JSON format:
|
|
|
1149
1189
|
import { Command as Command12 } from "commander";
|
|
1150
1190
|
import chalk12 from "chalk";
|
|
1151
1191
|
import ora11 from "ora";
|
|
1152
|
-
import { readFileSync as
|
|
1153
|
-
import { join as join5, basename } from "path";
|
|
1192
|
+
import { readFileSync as readFileSync3, existsSync as existsSync2, statSync as statSync2 } from "fs";
|
|
1193
|
+
import { join as join5, basename, resolve as resolve2, normalize as normalize2 } from "path";
|
|
1154
1194
|
import {
|
|
1155
1195
|
RLMEngine as RLMEngine3,
|
|
1156
1196
|
createGeminiClient as createGeminiClient5,
|
|
1157
1197
|
mergeFilesToContext as mergeFilesToContext3
|
|
1158
1198
|
} from "@contextos/core";
|
|
1199
|
+
function validatePath2(userPath, projectRoot) {
|
|
1200
|
+
const resolved = resolve2(projectRoot, userPath);
|
|
1201
|
+
const normalized = normalize2(resolved);
|
|
1202
|
+
const rootNormalized = normalize2(projectRoot);
|
|
1203
|
+
if (!normalized.startsWith(rootNormalized)) {
|
|
1204
|
+
throw new Error(`Path traversal detected: "${userPath}" escapes project boundaries`);
|
|
1205
|
+
}
|
|
1206
|
+
return normalized;
|
|
1207
|
+
}
|
|
1159
1208
|
var explainCommand = new Command12("explain").description("Get AI-powered explanation of code").argument("<target>", "File path, function name, or module to explain").option("-d, --depth <number>", "Include dependencies up to depth", "1").option("-f, --format <format>", "Output format: text, markdown, json", "markdown").option("--no-examples", "Skip usage examples in explanation").option("--technical", "Include technical implementation details").action(async (target, options) => {
|
|
1160
1209
|
console.log(chalk12.blue.bold("\n\u{1F4DA} ContextOS Code Explainer\n"));
|
|
1161
1210
|
const spinner = ora11("Analyzing target...").start();
|
|
@@ -1163,15 +1212,26 @@ var explainCommand = new Command12("explain").description("Get AI-powered explan
|
|
|
1163
1212
|
let targetContent = "";
|
|
1164
1213
|
let targetPath = "";
|
|
1165
1214
|
let contextFiles = [];
|
|
1215
|
+
let safePath;
|
|
1216
|
+
try {
|
|
1217
|
+
safePath = validatePath2(target, process.cwd());
|
|
1218
|
+
} catch (error) {
|
|
1219
|
+
if (error instanceof Error && error.message.includes("Path traversal")) {
|
|
1220
|
+
spinner.fail("Invalid path");
|
|
1221
|
+
console.log(chalk12.red("Error:"), error.message);
|
|
1222
|
+
process.exit(1);
|
|
1223
|
+
}
|
|
1224
|
+
throw error;
|
|
1225
|
+
}
|
|
1166
1226
|
const possiblePath = join5(process.cwd(), target);
|
|
1167
1227
|
if (existsSync2(possiblePath) && statSync2(possiblePath).isFile()) {
|
|
1168
1228
|
targetPath = target;
|
|
1169
|
-
targetContent =
|
|
1229
|
+
targetContent = readFileSync3(possiblePath, "utf-8");
|
|
1170
1230
|
contextFiles.push({ path: target, content: targetContent });
|
|
1171
1231
|
spinner.text = `Found file: ${target}`;
|
|
1172
|
-
} else if (existsSync2(
|
|
1232
|
+
} else if (existsSync2(safePath) && statSync2(safePath).isFile()) {
|
|
1173
1233
|
targetPath = target;
|
|
1174
|
-
targetContent =
|
|
1234
|
+
targetContent = readFileSync3(safePath, "utf-8");
|
|
1175
1235
|
contextFiles.push({ path: basename(target), content: targetContent });
|
|
1176
1236
|
spinner.text = `Found file: ${target}`;
|
|
1177
1237
|
} else {
|
|
@@ -1331,7 +1391,571 @@ var traceCommand = new Command13("trace").description("Trace function call chain
|
|
|
1331
1391
|
}
|
|
1332
1392
|
});
|
|
1333
1393
|
|
|
1394
|
+
// src/commands/plugin.ts
|
|
1395
|
+
import chalk14 from "chalk";
|
|
1396
|
+
import ora13 from "ora";
|
|
1397
|
+
import inquirer3 from "inquirer";
|
|
1398
|
+
import { createPluginManager, createPluginRegistry } from "@contextos/core";
|
|
1399
|
+
function registerPluginCommand(program2) {
|
|
1400
|
+
const plugin = program2.command("plugin").description("Manage ContextOS plugins");
|
|
1401
|
+
plugin.command("list").description("List installed plugins").option("-a, --all", "Include disabled plugins").option("--remote", "Show available plugins from registry").action(async (options) => {
|
|
1402
|
+
const cwd = process.cwd();
|
|
1403
|
+
const registry = createPluginRegistry(cwd);
|
|
1404
|
+
if (options.remote) {
|
|
1405
|
+
console.log(chalk14.cyan("\n\u{1F4E6} Featured Plugins:\n"));
|
|
1406
|
+
const featured = await registry.getFeatured();
|
|
1407
|
+
for (const plugin2 of featured) {
|
|
1408
|
+
const installed = registry.isInstalled(plugin2.name);
|
|
1409
|
+
const status = installed ? chalk14.green("\u2713 installed") : "";
|
|
1410
|
+
console.log(` ${chalk14.bold(plugin2.name)} ${chalk14.gray(`v${plugin2.version}`)} ${status}`);
|
|
1411
|
+
console.log(` ${plugin2.description}`);
|
|
1412
|
+
console.log(` ${chalk14.gray(`Downloads: ${plugin2.downloads}`)}
|
|
1413
|
+
`);
|
|
1414
|
+
}
|
|
1415
|
+
return;
|
|
1416
|
+
}
|
|
1417
|
+
const plugins = registry.listLocal();
|
|
1418
|
+
if (plugins.length === 0) {
|
|
1419
|
+
console.log(chalk14.yellow("\nNo plugins installed."));
|
|
1420
|
+
console.log(chalk14.gray("Run `ctx plugin list --remote` to see available plugins.\n"));
|
|
1421
|
+
return;
|
|
1422
|
+
}
|
|
1423
|
+
console.log(chalk14.cyan("\n\u{1F50C} Installed Plugins:\n"));
|
|
1424
|
+
for (const plugin2 of plugins) {
|
|
1425
|
+
if (!options.all && !plugin2.enabled) continue;
|
|
1426
|
+
const status = plugin2.enabled ? chalk14.green("\u25CF enabled") : chalk14.gray("\u25CB disabled");
|
|
1427
|
+
console.log(` ${status} ${chalk14.bold(plugin2.name)} ${chalk14.gray(`v${plugin2.version}`)}`);
|
|
1428
|
+
console.log(` ${plugin2.description || "No description"}`);
|
|
1429
|
+
console.log(` ${chalk14.gray(plugin2.path)}
|
|
1430
|
+
`);
|
|
1431
|
+
}
|
|
1432
|
+
});
|
|
1433
|
+
plugin.command("install <source>").description("Install a plugin").option("-l, --local", "Install from local path").option("-f, --force", "Force reinstall").action(async (source, options) => {
|
|
1434
|
+
const spinner = ora13("Installing plugin...").start();
|
|
1435
|
+
try {
|
|
1436
|
+
const cwd = process.cwd();
|
|
1437
|
+
const manager = createPluginManager(cwd);
|
|
1438
|
+
const plugin2 = await manager.install(source, {
|
|
1439
|
+
local: options.local,
|
|
1440
|
+
force: options.force
|
|
1441
|
+
});
|
|
1442
|
+
spinner.succeed(`Installed ${chalk14.bold(plugin2.name)} v${plugin2.version}`);
|
|
1443
|
+
} catch (error) {
|
|
1444
|
+
spinner.fail(`Failed to install plugin`);
|
|
1445
|
+
console.error(chalk14.red(error instanceof Error ? error.message : String(error)));
|
|
1446
|
+
process.exit(1);
|
|
1447
|
+
}
|
|
1448
|
+
});
|
|
1449
|
+
plugin.command("remove <name>").alias("uninstall").description("Remove a plugin").option("-f, --force", "Skip confirmation").action(async (name, options) => {
|
|
1450
|
+
const cwd = process.cwd();
|
|
1451
|
+
const registry = createPluginRegistry(cwd);
|
|
1452
|
+
const localPlugin = registry.getLocal(name);
|
|
1453
|
+
if (!localPlugin) {
|
|
1454
|
+
console.log(chalk14.red(`Plugin not found: ${name}`));
|
|
1455
|
+
process.exit(1);
|
|
1456
|
+
}
|
|
1457
|
+
if (!options.force) {
|
|
1458
|
+
const { confirm } = await inquirer3.prompt([
|
|
1459
|
+
{
|
|
1460
|
+
type: "confirm",
|
|
1461
|
+
name: "confirm",
|
|
1462
|
+
message: `Remove plugin ${chalk14.bold(name)}?`,
|
|
1463
|
+
default: false
|
|
1464
|
+
}
|
|
1465
|
+
]);
|
|
1466
|
+
if (!confirm) {
|
|
1467
|
+
console.log(chalk14.gray("Cancelled."));
|
|
1468
|
+
return;
|
|
1469
|
+
}
|
|
1470
|
+
}
|
|
1471
|
+
const spinner = ora13("Removing plugin...").start();
|
|
1472
|
+
try {
|
|
1473
|
+
const manager = createPluginManager(cwd);
|
|
1474
|
+
await manager.uninstall(name);
|
|
1475
|
+
spinner.succeed(`Removed ${chalk14.bold(name)}`);
|
|
1476
|
+
} catch (error) {
|
|
1477
|
+
spinner.fail(`Failed to remove plugin`);
|
|
1478
|
+
console.error(chalk14.red(error instanceof Error ? error.message : String(error)));
|
|
1479
|
+
process.exit(1);
|
|
1480
|
+
}
|
|
1481
|
+
});
|
|
1482
|
+
plugin.command("enable <name>").description("Enable a disabled plugin").action(async (name) => {
|
|
1483
|
+
const cwd = process.cwd();
|
|
1484
|
+
const manager = createPluginManager(cwd);
|
|
1485
|
+
await manager.loadAll();
|
|
1486
|
+
const success = await manager.enablePlugin(name);
|
|
1487
|
+
if (success) {
|
|
1488
|
+
console.log(chalk14.green(`\u2713 Enabled ${chalk14.bold(name)}`));
|
|
1489
|
+
} else {
|
|
1490
|
+
console.log(chalk14.red(`Plugin not found: ${name}`));
|
|
1491
|
+
process.exit(1);
|
|
1492
|
+
}
|
|
1493
|
+
});
|
|
1494
|
+
plugin.command("disable <name>").description("Disable a plugin").action(async (name) => {
|
|
1495
|
+
const cwd = process.cwd();
|
|
1496
|
+
const manager = createPluginManager(cwd);
|
|
1497
|
+
await manager.loadAll();
|
|
1498
|
+
const success = await manager.disablePlugin(name);
|
|
1499
|
+
if (success) {
|
|
1500
|
+
console.log(chalk14.green(`\u2713 Disabled ${chalk14.bold(name)}`));
|
|
1501
|
+
} else {
|
|
1502
|
+
console.log(chalk14.red(`Plugin not found: ${name}`));
|
|
1503
|
+
process.exit(1);
|
|
1504
|
+
}
|
|
1505
|
+
});
|
|
1506
|
+
plugin.command("create <name>").description("Create a new plugin scaffold").option("-d, --description <desc>", "Plugin description").option("-a, --author <author>", "Plugin author").option("--with-commands", "Include command examples").action(async (name, options) => {
|
|
1507
|
+
const cwd = process.cwd();
|
|
1508
|
+
const answers = await inquirer3.prompt([
|
|
1509
|
+
{
|
|
1510
|
+
type: "input",
|
|
1511
|
+
name: "description",
|
|
1512
|
+
message: "Plugin description:",
|
|
1513
|
+
default: options.description || `A ContextOS plugin`,
|
|
1514
|
+
when: !options.description
|
|
1515
|
+
},
|
|
1516
|
+
{
|
|
1517
|
+
type: "input",
|
|
1518
|
+
name: "author",
|
|
1519
|
+
message: "Author:",
|
|
1520
|
+
default: options.author || process.env.USER || "Anonymous",
|
|
1521
|
+
when: !options.author
|
|
1522
|
+
},
|
|
1523
|
+
{
|
|
1524
|
+
type: "checkbox",
|
|
1525
|
+
name: "hooks",
|
|
1526
|
+
message: "Select hooks to implement:",
|
|
1527
|
+
choices: [
|
|
1528
|
+
{ name: "onBeforeContextBuild", value: "onBeforeContextBuild" },
|
|
1529
|
+
{ name: "onAfterContextBuild", value: "onAfterContextBuild" },
|
|
1530
|
+
{ name: "onBeforeIndex", value: "onBeforeIndex" },
|
|
1531
|
+
{ name: "onAfterIndex", value: "onAfterIndex" },
|
|
1532
|
+
{ name: "onBeforeAnalyze", value: "onBeforeAnalyze" },
|
|
1533
|
+
{ name: "onAfterAnalyze", value: "onAfterAnalyze" },
|
|
1534
|
+
{ name: "fileFilter", value: "fileFilter" },
|
|
1535
|
+
{ name: "rankingBoost", value: "rankingBoost" }
|
|
1536
|
+
],
|
|
1537
|
+
default: ["onAfterContextBuild"]
|
|
1538
|
+
}
|
|
1539
|
+
]);
|
|
1540
|
+
const template = {
|
|
1541
|
+
name,
|
|
1542
|
+
description: options.description || answers.description,
|
|
1543
|
+
author: options.author || answers.author,
|
|
1544
|
+
hooks: answers.hooks,
|
|
1545
|
+
withCommands: options.withCommands || false
|
|
1546
|
+
};
|
|
1547
|
+
const spinner = ora13("Creating plugin scaffold...").start();
|
|
1548
|
+
try {
|
|
1549
|
+
const manager = createPluginManager(cwd);
|
|
1550
|
+
const pluginPath = manager.createPluginScaffold(template);
|
|
1551
|
+
spinner.succeed(`Created plugin at ${chalk14.cyan(pluginPath)}`);
|
|
1552
|
+
console.log(`
|
|
1553
|
+
${chalk14.gray("Next steps:")}`);
|
|
1554
|
+
console.log(` 1. Edit ${chalk14.cyan(`${pluginPath}/index.js`)}`);
|
|
1555
|
+
console.log(` 2. Run ${chalk14.cyan(`ctx plugin enable ${name}`)}`);
|
|
1556
|
+
console.log(` 3. Test your plugin!
|
|
1557
|
+
`);
|
|
1558
|
+
} catch (error) {
|
|
1559
|
+
spinner.fail(`Failed to create plugin`);
|
|
1560
|
+
console.error(chalk14.red(error instanceof Error ? error.message : String(error)));
|
|
1561
|
+
process.exit(1);
|
|
1562
|
+
}
|
|
1563
|
+
});
|
|
1564
|
+
plugin.command("run <plugin> <command> [args...]").description("Run a custom command from a plugin").action(async (pluginName, command, args) => {
|
|
1565
|
+
const cwd = process.cwd();
|
|
1566
|
+
const manager = createPluginManager(cwd);
|
|
1567
|
+
await manager.loadAll();
|
|
1568
|
+
const pluginState = manager.get(pluginName);
|
|
1569
|
+
if (!pluginState) {
|
|
1570
|
+
console.log(chalk14.red(`Plugin not found: ${pluginName}`));
|
|
1571
|
+
process.exit(1);
|
|
1572
|
+
}
|
|
1573
|
+
const cmd = pluginState.instance.commands?.[command];
|
|
1574
|
+
if (!cmd) {
|
|
1575
|
+
console.log(chalk14.red(`Command not found: ${command}`));
|
|
1576
|
+
console.log(chalk14.gray("Available commands:"));
|
|
1577
|
+
const commands = Object.keys(pluginState.instance.commands || {});
|
|
1578
|
+
if (commands.length === 0) {
|
|
1579
|
+
console.log(chalk14.gray(" (none)"));
|
|
1580
|
+
} else {
|
|
1581
|
+
for (const c of commands) {
|
|
1582
|
+
console.log(` ${c}`);
|
|
1583
|
+
}
|
|
1584
|
+
}
|
|
1585
|
+
process.exit(1);
|
|
1586
|
+
}
|
|
1587
|
+
try {
|
|
1588
|
+
const context = {
|
|
1589
|
+
projectRoot: cwd,
|
|
1590
|
+
configDir: `${cwd}/.contextos`,
|
|
1591
|
+
log: {
|
|
1592
|
+
debug: console.debug,
|
|
1593
|
+
info: console.info,
|
|
1594
|
+
warn: console.warn,
|
|
1595
|
+
error: console.error
|
|
1596
|
+
},
|
|
1597
|
+
query: async () => ({ files: [], context: "" }),
|
|
1598
|
+
readFile: async () => "",
|
|
1599
|
+
getDependencies: async () => [],
|
|
1600
|
+
storage: {
|
|
1601
|
+
get: () => void 0,
|
|
1602
|
+
set: () => {
|
|
1603
|
+
},
|
|
1604
|
+
delete: () => false
|
|
1605
|
+
}
|
|
1606
|
+
};
|
|
1607
|
+
await cmd.handler(args, context);
|
|
1608
|
+
} catch (error) {
|
|
1609
|
+
console.error(chalk14.red(`Command failed: ${error instanceof Error ? error.message : String(error)}`));
|
|
1610
|
+
process.exit(1);
|
|
1611
|
+
}
|
|
1612
|
+
});
|
|
1613
|
+
}
|
|
1614
|
+
|
|
1615
|
+
// src/commands/finetune.ts
|
|
1616
|
+
import chalk15 from "chalk";
|
|
1617
|
+
import ora14 from "ora";
|
|
1618
|
+
import {
|
|
1619
|
+
createTrainingDataCollector,
|
|
1620
|
+
createDatasetFormatter
|
|
1621
|
+
} from "@contextos/core";
|
|
1622
|
+
function registerFinetuneCommand(program2) {
|
|
1623
|
+
const finetune = program2.command("finetune").description("Training data management for model fine-tuning");
|
|
1624
|
+
finetune.command("export").description("Export training data to file").option("-o, --output <path>", "Output file path", "./training-data.jsonl").option("-f, --format <format>", "Output format (jsonl, openai, anthropic, csv)", "jsonl").option("-m, --max <count>", "Maximum examples to export", parseInt).option("-r, --min-rating <rating>", "Minimum rating (good, neutral, bad)").option("-l, --language <lang>", "Filter by language").option("--shuffle", "Shuffle examples").option("--split <ratio>", "Train/validation split ratio", parseFloat).action(async (options) => {
|
|
1625
|
+
const cwd = process.cwd();
|
|
1626
|
+
const spinner = ora14("Exporting training data...").start();
|
|
1627
|
+
try {
|
|
1628
|
+
const collector = createTrainingDataCollector(cwd);
|
|
1629
|
+
const formatter = createDatasetFormatter();
|
|
1630
|
+
const examples = collector.getAll();
|
|
1631
|
+
if (examples.length === 0) {
|
|
1632
|
+
spinner.warn("No training data found");
|
|
1633
|
+
console.log(chalk15.gray("\nTraining data is collected automatically when you use ctx goal/build commands."));
|
|
1634
|
+
return;
|
|
1635
|
+
}
|
|
1636
|
+
const format = {
|
|
1637
|
+
format: options.format
|
|
1638
|
+
};
|
|
1639
|
+
const config = {
|
|
1640
|
+
maxExamples: options.max,
|
|
1641
|
+
minRating: options.minRating,
|
|
1642
|
+
languageFilter: options.language,
|
|
1643
|
+
shuffle: options.shuffle,
|
|
1644
|
+
validationSplit: options.split
|
|
1645
|
+
};
|
|
1646
|
+
if (options.split) {
|
|
1647
|
+
const outputDir = options.output.replace(/\.[^.]+$/, "");
|
|
1648
|
+
const result = await formatter.exportWithSplit(
|
|
1649
|
+
examples,
|
|
1650
|
+
outputDir,
|
|
1651
|
+
format,
|
|
1652
|
+
config
|
|
1653
|
+
);
|
|
1654
|
+
spinner.succeed("Export complete");
|
|
1655
|
+
console.log(`
|
|
1656
|
+
Train: ${chalk15.cyan(result.train)} examples \u2192 ${outputDir}/train.jsonl`);
|
|
1657
|
+
console.log(` Validation: ${chalk15.cyan(result.validation)} examples \u2192 ${outputDir}/validation.jsonl`);
|
|
1658
|
+
} else {
|
|
1659
|
+
const result = await formatter.export(
|
|
1660
|
+
examples,
|
|
1661
|
+
options.output,
|
|
1662
|
+
format,
|
|
1663
|
+
config
|
|
1664
|
+
);
|
|
1665
|
+
spinner.succeed(`Exported ${chalk15.cyan(result.exported)} examples to ${chalk15.cyan(result.path)}`);
|
|
1666
|
+
}
|
|
1667
|
+
} catch (error) {
|
|
1668
|
+
spinner.fail("Export failed");
|
|
1669
|
+
console.error(chalk15.red(error instanceof Error ? error.message : String(error)));
|
|
1670
|
+
process.exit(1);
|
|
1671
|
+
}
|
|
1672
|
+
});
|
|
1673
|
+
finetune.command("validate <file>").description("Validate a training data file").action(async (file) => {
|
|
1674
|
+
const spinner = ora14("Validating dataset...").start();
|
|
1675
|
+
try {
|
|
1676
|
+
const formatter = createDatasetFormatter();
|
|
1677
|
+
const result = await formatter.validate(file);
|
|
1678
|
+
if (result.valid) {
|
|
1679
|
+
spinner.succeed("Dataset is valid");
|
|
1680
|
+
} else {
|
|
1681
|
+
spinner.fail("Dataset has errors");
|
|
1682
|
+
}
|
|
1683
|
+
console.log(`
|
|
1684
|
+
${chalk15.bold("Statistics:")}`);
|
|
1685
|
+
console.log(` Total examples: ${chalk15.cyan(result.stats.totalExamples)}`);
|
|
1686
|
+
console.log(` Avg tokens: ${chalk15.cyan(result.stats.avgTokenCount)}`);
|
|
1687
|
+
console.log(` Avg files/example: ${chalk15.cyan(result.stats.avgFilesPerExample)}`);
|
|
1688
|
+
console.log(`
|
|
1689
|
+
${chalk15.bold("Rating Distribution:")}`);
|
|
1690
|
+
console.log(` ${chalk15.green("Good")}: ${result.stats.ratingDistribution.good}`);
|
|
1691
|
+
console.log(` ${chalk15.yellow("Neutral")}: ${result.stats.ratingDistribution.neutral}`);
|
|
1692
|
+
console.log(` ${chalk15.red("Bad")}: ${result.stats.ratingDistribution.bad}`);
|
|
1693
|
+
console.log(` ${chalk15.gray("Unrated")}: ${result.stats.ratingDistribution.unrated}`);
|
|
1694
|
+
if (Object.keys(result.stats.languageDistribution).length > 0) {
|
|
1695
|
+
console.log(`
|
|
1696
|
+
${chalk15.bold("Languages:")}`);
|
|
1697
|
+
for (const [lang, count] of Object.entries(result.stats.languageDistribution)) {
|
|
1698
|
+
console.log(` ${lang}: ${count}`);
|
|
1699
|
+
}
|
|
1700
|
+
}
|
|
1701
|
+
if (result.errors.length > 0) {
|
|
1702
|
+
console.log(`
|
|
1703
|
+
${chalk15.red.bold("Errors:")}`);
|
|
1704
|
+
for (const error of result.errors.slice(0, 10)) {
|
|
1705
|
+
console.log(` Line ${error.line}: ${error.message}`);
|
|
1706
|
+
}
|
|
1707
|
+
if (result.errors.length > 10) {
|
|
1708
|
+
console.log(chalk15.gray(` ... and ${result.errors.length - 10} more`));
|
|
1709
|
+
}
|
|
1710
|
+
}
|
|
1711
|
+
if (result.warnings.length > 0) {
|
|
1712
|
+
console.log(`
|
|
1713
|
+
${chalk15.yellow.bold("Warnings:")}`);
|
|
1714
|
+
for (const warning of result.warnings) {
|
|
1715
|
+
console.log(` \u26A0 ${warning}`);
|
|
1716
|
+
}
|
|
1717
|
+
}
|
|
1718
|
+
} catch (error) {
|
|
1719
|
+
spinner.fail("Validation failed");
|
|
1720
|
+
console.error(chalk15.red(error instanceof Error ? error.message : String(error)));
|
|
1721
|
+
process.exit(1);
|
|
1722
|
+
}
|
|
1723
|
+
});
|
|
1724
|
+
finetune.command("stats").description("Show training data statistics").action(async () => {
|
|
1725
|
+
const cwd = process.cwd();
|
|
1726
|
+
const collector = createTrainingDataCollector(cwd);
|
|
1727
|
+
const stats = collector.getStats();
|
|
1728
|
+
console.log(chalk15.cyan.bold("\n\u{1F4CA} Training Data Statistics\n"));
|
|
1729
|
+
if (stats.totalExamples === 0) {
|
|
1730
|
+
console.log(chalk15.yellow("No training data collected yet."));
|
|
1731
|
+
console.log(chalk15.gray("\nTraining data is collected when you use ctx goal/build commands.\n"));
|
|
1732
|
+
return;
|
|
1733
|
+
}
|
|
1734
|
+
console.log(`${chalk15.bold("Total Examples:")} ${stats.totalExamples}`);
|
|
1735
|
+
console.log(`${chalk15.bold("Avg Token Count:")} ${stats.avgTokenCount}`);
|
|
1736
|
+
console.log(`${chalk15.bold("Avg Files/Example:")} ${stats.avgFilesPerExample}`);
|
|
1737
|
+
console.log(`
|
|
1738
|
+
${chalk15.bold("Rating Distribution:")}`);
|
|
1739
|
+
const total = stats.totalExamples;
|
|
1740
|
+
const pct = (n) => (n / total * 100).toFixed(1);
|
|
1741
|
+
console.log(` ${chalk15.green("\u25CF")} Good: ${stats.ratingDistribution.good} (${pct(stats.ratingDistribution.good)}%)`);
|
|
1742
|
+
console.log(` ${chalk15.yellow("\u25CF")} Neutral: ${stats.ratingDistribution.neutral} (${pct(stats.ratingDistribution.neutral)}%)`);
|
|
1743
|
+
console.log(` ${chalk15.red("\u25CF")} Bad: ${stats.ratingDistribution.bad} (${pct(stats.ratingDistribution.bad)}%)`);
|
|
1744
|
+
console.log(` ${chalk15.gray("\u25CB")} Unrated: ${stats.ratingDistribution.unrated} (${pct(stats.ratingDistribution.unrated)}%)`);
|
|
1745
|
+
console.log(`
|
|
1746
|
+
${chalk15.bold("Languages:")}`);
|
|
1747
|
+
for (const [lang, count] of Object.entries(stats.languageDistribution)) {
|
|
1748
|
+
console.log(` ${lang}: ${count} (${pct(count)}%)`);
|
|
1749
|
+
}
|
|
1750
|
+
console.log(`
|
|
1751
|
+
${chalk15.bold("Date Range:")}`);
|
|
1752
|
+
console.log(` From: ${stats.dateRange.earliest.toLocaleDateString()}`);
|
|
1753
|
+
console.log(` To: ${stats.dateRange.latest.toLocaleDateString()}`);
|
|
1754
|
+
console.log();
|
|
1755
|
+
});
|
|
1756
|
+
finetune.command("feedback <id> <rating>").description("Add feedback to a training example (good/bad/neutral)").option("-c, --comment <comment>", "Optional comment").action(async (id, rating, options) => {
|
|
1757
|
+
const validRatings = ["good", "bad", "neutral"];
|
|
1758
|
+
if (!validRatings.includes(rating)) {
|
|
1759
|
+
console.error(chalk15.red(`Invalid rating. Use: ${validRatings.join(", ")}`));
|
|
1760
|
+
process.exit(1);
|
|
1761
|
+
}
|
|
1762
|
+
const cwd = process.cwd();
|
|
1763
|
+
const collector = createTrainingDataCollector(cwd);
|
|
1764
|
+
const success = collector.addFeedback(
|
|
1765
|
+
id,
|
|
1766
|
+
rating,
|
|
1767
|
+
options.comment
|
|
1768
|
+
);
|
|
1769
|
+
if (success) {
|
|
1770
|
+
console.log(chalk15.green(`\u2713 Feedback added for ${id}`));
|
|
1771
|
+
} else {
|
|
1772
|
+
console.error(chalk15.red(`Example not found: ${id}`));
|
|
1773
|
+
process.exit(1);
|
|
1774
|
+
}
|
|
1775
|
+
});
|
|
1776
|
+
finetune.command("recent").description("Show recent training examples").option("-n, --limit <count>", "Number of examples", parseInt, 5).action(async (options) => {
|
|
1777
|
+
const cwd = process.cwd();
|
|
1778
|
+
const collector = createTrainingDataCollector(cwd);
|
|
1779
|
+
const recent = collector.getRecent(options.limit);
|
|
1780
|
+
if (recent.length === 0) {
|
|
1781
|
+
console.log(chalk15.yellow("\nNo training data collected yet.\n"));
|
|
1782
|
+
return;
|
|
1783
|
+
}
|
|
1784
|
+
console.log(chalk15.cyan.bold("\n\u{1F4CB} Recent Training Examples\n"));
|
|
1785
|
+
for (const example of recent) {
|
|
1786
|
+
const rating = example.feedback?.rating;
|
|
1787
|
+
const ratingIcon = rating === "good" ? "\u{1F44D}" : rating === "bad" ? "\u{1F44E}" : "\u2022";
|
|
1788
|
+
const date = new Date(example.meta.timestamp).toLocaleString();
|
|
1789
|
+
console.log(`${chalk15.bold(example.id)} ${ratingIcon}`);
|
|
1790
|
+
console.log(` Goal: ${chalk15.white(example.goal.substring(0, 60))}${example.goal.length > 60 ? "..." : ""}`);
|
|
1791
|
+
console.log(` Files: ${example.selectedFiles.length} | Tokens: ${example.tokenCount}`);
|
|
1792
|
+
console.log(` ${chalk15.gray(date)}
|
|
1793
|
+
`);
|
|
1794
|
+
}
|
|
1795
|
+
});
|
|
1796
|
+
}
|
|
1797
|
+
|
|
1798
|
+
// src/commands/cloud.ts
|
|
1799
|
+
import chalk16 from "chalk";
|
|
1800
|
+
import ora15 from "ora";
|
|
1801
|
+
import { existsSync as existsSync3, readFileSync as readFileSync4, writeFileSync as writeFileSync4, mkdirSync as mkdirSync2 } from "fs";
|
|
1802
|
+
import { homedir } from "os";
|
|
1803
|
+
import { join as join6 } from "path";
|
|
1804
|
+
var CLOUD_CONFIG_PATH = join6(homedir(), ".contextos", "cloud.json");
|
|
1805
|
+
var DEFAULT_CLOUD_URL = "https://api.contextos.dev";
|
|
1806
|
+
function loadCloudConfig() {
|
|
1807
|
+
if (existsSync3(CLOUD_CONFIG_PATH)) {
|
|
1808
|
+
try {
|
|
1809
|
+
return JSON.parse(readFileSync4(CLOUD_CONFIG_PATH, "utf-8"));
|
|
1810
|
+
} catch {
|
|
1811
|
+
return { cloudUrl: DEFAULT_CLOUD_URL };
|
|
1812
|
+
}
|
|
1813
|
+
}
|
|
1814
|
+
return { cloudUrl: DEFAULT_CLOUD_URL };
|
|
1815
|
+
}
|
|
1816
|
+
function saveCloudConfig(config) {
|
|
1817
|
+
const dir = join6(CLOUD_CONFIG_PATH, "..");
|
|
1818
|
+
if (!existsSync3(dir)) {
|
|
1819
|
+
mkdirSync2(dir, { recursive: true });
|
|
1820
|
+
}
|
|
1821
|
+
writeFileSync4(CLOUD_CONFIG_PATH, JSON.stringify(config, null, 2));
|
|
1822
|
+
}
|
|
1823
|
+
function registerCloudCommands(program2) {
|
|
1824
|
+
program2.command("login").description("Login to ContextOS Cloud").option("--api-key <key>", "API key").option("--url <url>", "Cloud server URL", DEFAULT_CLOUD_URL).action(async (options) => {
|
|
1825
|
+
console.log(chalk16.cyan.bold("\n\u{1F510} ContextOS Cloud Login\n"));
|
|
1826
|
+
let apiKey = options.apiKey;
|
|
1827
|
+
if (!apiKey) {
|
|
1828
|
+
const { default: inquirer4 } = await import("inquirer");
|
|
1829
|
+
const answers = await inquirer4.prompt([
|
|
1830
|
+
{
|
|
1831
|
+
type: "input",
|
|
1832
|
+
name: "apiKey",
|
|
1833
|
+
message: "Enter your API key:",
|
|
1834
|
+
validate: (input) => input.startsWith("ctx_") || "API key must start with ctx_"
|
|
1835
|
+
}
|
|
1836
|
+
]);
|
|
1837
|
+
apiKey = answers.apiKey;
|
|
1838
|
+
}
|
|
1839
|
+
const spinner = ora15("Validating API key...").start();
|
|
1840
|
+
try {
|
|
1841
|
+
const response = await fetch(`${options.url}/health`, {
|
|
1842
|
+
headers: { "X-API-Key": apiKey }
|
|
1843
|
+
});
|
|
1844
|
+
if (!response.ok) {
|
|
1845
|
+
spinner.fail("Invalid API key or server error");
|
|
1846
|
+
return;
|
|
1847
|
+
}
|
|
1848
|
+
spinner.succeed("API key validated");
|
|
1849
|
+
const config = {
|
|
1850
|
+
apiKey,
|
|
1851
|
+
cloudUrl: options.url,
|
|
1852
|
+
userId: `user_${apiKey.slice(4, 12)}`,
|
|
1853
|
+
tier: "free"
|
|
1854
|
+
};
|
|
1855
|
+
saveCloudConfig(config);
|
|
1856
|
+
console.log(chalk16.green("\n\u2705 Logged in successfully!"));
|
|
1857
|
+
console.log(chalk16.gray(`Config saved to: ${CLOUD_CONFIG_PATH}
|
|
1858
|
+
`));
|
|
1859
|
+
} catch (error) {
|
|
1860
|
+
spinner.fail(error instanceof Error ? error.message : "Connection failed");
|
|
1861
|
+
}
|
|
1862
|
+
});
|
|
1863
|
+
program2.command("logout").description("Logout from ContextOS Cloud").action(() => {
|
|
1864
|
+
const config = loadCloudConfig();
|
|
1865
|
+
config.apiKey = void 0;
|
|
1866
|
+
config.userId = void 0;
|
|
1867
|
+
saveCloudConfig(config);
|
|
1868
|
+
console.log(chalk16.green("\n\u2705 Logged out successfully\n"));
|
|
1869
|
+
});
|
|
1870
|
+
program2.command("connect").description("Connect your IDEs to ContextOS Cloud").option("--all", "Configure all detected IDEs").option("--ide <name>", "Configure specific IDE").action(async (options) => {
|
|
1871
|
+
const config = loadCloudConfig();
|
|
1872
|
+
if (!config.apiKey) {
|
|
1873
|
+
console.log(chalk16.red("\n\u274C Not logged in. Run `ctx login` first.\n"));
|
|
1874
|
+
return;
|
|
1875
|
+
}
|
|
1876
|
+
console.log(chalk16.cyan.bold("\n\u{1F50C} Connecting IDEs to ContextOS Cloud\n"));
|
|
1877
|
+
const { detectIDEs } = await import("@contextos/setup").catch(() => ({
|
|
1878
|
+
detectIDEs: async () => []
|
|
1879
|
+
}));
|
|
1880
|
+
const ides = await detectIDEs();
|
|
1881
|
+
if (ides.length === 0) {
|
|
1882
|
+
console.log(chalk16.yellow("No supported IDEs detected.\n"));
|
|
1883
|
+
return;
|
|
1884
|
+
}
|
|
1885
|
+
console.log(chalk16.gray("This will configure your IDEs to use the cloud MCP server."));
|
|
1886
|
+
console.log(chalk16.gray(`Cloud URL: ${config.cloudUrl}
|
|
1887
|
+
`));
|
|
1888
|
+
for (const ide of ides) {
|
|
1889
|
+
if (options.ide && ide.id !== options.ide) continue;
|
|
1890
|
+
console.log(`${chalk16.cyan("\u2192")} ${ide.name}`);
|
|
1891
|
+
}
|
|
1892
|
+
console.log(chalk16.green("\n\u2705 IDEs connected to cloud!\n"));
|
|
1893
|
+
});
|
|
1894
|
+
program2.command("cloud").description("Show ContextOS Cloud status").action(async () => {
|
|
1895
|
+
const config = loadCloudConfig();
|
|
1896
|
+
console.log(chalk16.cyan.bold("\n\u2601\uFE0F ContextOS Cloud Status\n"));
|
|
1897
|
+
if (!config.apiKey) {
|
|
1898
|
+
console.log(chalk16.yellow("Not logged in"));
|
|
1899
|
+
console.log(chalk16.gray("\nRun `ctx login` to connect to ContextOS Cloud.\n"));
|
|
1900
|
+
return;
|
|
1901
|
+
}
|
|
1902
|
+
console.log(`${chalk16.gray("User ID:")} ${config.userId}`);
|
|
1903
|
+
console.log(`${chalk16.gray("Tier:")} ${config.tier}`);
|
|
1904
|
+
console.log(`${chalk16.gray("Cloud URL:")} ${config.cloudUrl}`);
|
|
1905
|
+
const spinner = ora15("Checking connection...").start();
|
|
1906
|
+
try {
|
|
1907
|
+
const response = await fetch(`${config.cloudUrl}/health`, {
|
|
1908
|
+
headers: { "X-API-Key": config.apiKey }
|
|
1909
|
+
});
|
|
1910
|
+
if (response.ok) {
|
|
1911
|
+
const data = await response.json();
|
|
1912
|
+
spinner.succeed(`Connected (v${data.version})`);
|
|
1913
|
+
} else {
|
|
1914
|
+
spinner.warn("Connection issues");
|
|
1915
|
+
}
|
|
1916
|
+
} catch {
|
|
1917
|
+
spinner.fail("Cannot reach cloud server");
|
|
1918
|
+
}
|
|
1919
|
+
console.log();
|
|
1920
|
+
});
|
|
1921
|
+
}
|
|
1922
|
+
|
|
1334
1923
|
// src/index.ts
|
|
1924
|
+
var crashLogDir = join7(process.cwd(), ".contextos");
|
|
1925
|
+
if (!existsSync4(crashLogDir)) {
|
|
1926
|
+
mkdirSync3(crashLogDir, { recursive: true });
|
|
1927
|
+
}
|
|
1928
|
+
var crashLogPath = join7(crashLogDir, "crash.log");
|
|
1929
|
+
function logCrash(message, data) {
|
|
1930
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
1931
|
+
const logEntry = `[${timestamp}] ${message}${data ? `
|
|
1932
|
+
${data}
|
|
1933
|
+
|
|
1934
|
+
` : "\n\n"}`;
|
|
1935
|
+
console.error(logEntry);
|
|
1936
|
+
appendFileSync(crashLogPath, logEntry, "utf-8");
|
|
1937
|
+
}
|
|
1938
|
+
process.on("uncaughtException", (error) => {
|
|
1939
|
+
console.error("\u274C UNCAUGHT EXCEPTION:", error.message);
|
|
1940
|
+
console.error("Stack:", error.stack);
|
|
1941
|
+
if (process.env.NODE_ENV === "production") {
|
|
1942
|
+
logCrash(`UNCAUGHT EXCEPTION: ${error.message}`, error.stack);
|
|
1943
|
+
}
|
|
1944
|
+
setTimeout(() => process.exit(1), 1e3);
|
|
1945
|
+
});
|
|
1946
|
+
process.on("unhandledRejection", (reason) => {
|
|
1947
|
+
console.error("\u274C UNHANDLED REJECTION:", reason);
|
|
1948
|
+
if (process.env.NODE_ENV === "production") {
|
|
1949
|
+
const reasonStr = reason instanceof Error ? reason.stack : String(reason);
|
|
1950
|
+
logCrash("UNHANDLED REJECTION", reasonStr);
|
|
1951
|
+
}
|
|
1952
|
+
});
|
|
1953
|
+
process.on("multipleResolves", (type, promise) => {
|
|
1954
|
+
console.warn("\u26A0\uFE0F MULTIPLE RESOLVES:", type, promise);
|
|
1955
|
+
});
|
|
1956
|
+
process.on("warning", (warning) => {
|
|
1957
|
+
console.warn("\u26A0\uFE0F PROCESS WARNING:", warning.name, warning.message);
|
|
1958
|
+
});
|
|
1335
1959
|
var program = new Command14();
|
|
1336
1960
|
program.name("ctx").description("ContextOS - The Context Server Protocol for AI Coding").version("0.1.0").option("-v, --verbose", "Enable verbose output");
|
|
1337
1961
|
program.addCommand(initCommand);
|
|
@@ -1347,4 +1971,7 @@ program.addCommand(analyzeCommand);
|
|
|
1347
1971
|
program.addCommand(refactorCommand);
|
|
1348
1972
|
program.addCommand(explainCommand);
|
|
1349
1973
|
program.addCommand(traceCommand);
|
|
1974
|
+
registerPluginCommand(program);
|
|
1975
|
+
registerFinetuneCommand(program);
|
|
1976
|
+
registerCloudCommands(program);
|
|
1350
1977
|
program.parse();
|
package/package.json
CHANGED
|
@@ -1,42 +1,42 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
"
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
"
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
2
|
+
"name": "@contextos/cli",
|
|
3
|
+
"version": "0.2.2",
|
|
4
|
+
"description": "CLI for ContextOS - The Context Server Protocol for AI Coding",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"ctx": "./dist/index.js"
|
|
8
|
+
},
|
|
9
|
+
"main": "./dist/index.js",
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"dependencies": {
|
|
12
|
+
"chalk": "^5.3.0",
|
|
13
|
+
"clipboardy": "^4.0.0",
|
|
14
|
+
"commander": "^12.0.0",
|
|
15
|
+
"glob": "^10.3.0",
|
|
16
|
+
"inquirer": "^9.2.0",
|
|
17
|
+
"ora": "^8.0.0",
|
|
18
|
+
"yaml": "^2.4.0",
|
|
19
|
+
"@contextos/core": "0.2.2"
|
|
20
|
+
},
|
|
21
|
+
"devDependencies": {
|
|
22
|
+
"@types/inquirer": "^9.0.7",
|
|
23
|
+
"@types/node": "^20.11.0",
|
|
24
|
+
"eslint": "^8.57.0",
|
|
25
|
+
"rimraf": "^5.0.5",
|
|
26
|
+
"tsup": "^8.0.2",
|
|
27
|
+
"typescript": "^5.4.0",
|
|
28
|
+
"vitest": "^1.4.0"
|
|
29
|
+
},
|
|
30
|
+
"files": [
|
|
31
|
+
"dist"
|
|
32
|
+
],
|
|
33
|
+
"license": "MIT",
|
|
34
|
+
"scripts": {
|
|
35
|
+
"build": "tsup src/index.ts --format esm --clean",
|
|
36
|
+
"dev": "tsup src/index.ts --format cjs --dts --watch",
|
|
37
|
+
"start": "node dist/index.js",
|
|
38
|
+
"test": "vitest run",
|
|
39
|
+
"lint": "eslint src/",
|
|
40
|
+
"clean": "rimraf dist"
|
|
41
|
+
}
|
|
42
42
|
}
|