@jvittechs/jai1-cli 0.1.100 → 0.1.102
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/cli.js +551 -122
- package/dist/cli.js.map +1 -1
- package/dist/web-chat/app.js +490 -5
- package/dist/web-chat/style.css +227 -0
- package/package.json +2 -1
package/dist/cli.js
CHANGED
|
@@ -33,7 +33,7 @@ var NetworkError = class extends Jai1Error {
|
|
|
33
33
|
// package.json
|
|
34
34
|
var package_default = {
|
|
35
35
|
name: "@jvittechs/jai1-cli",
|
|
36
|
-
version: "0.1.
|
|
36
|
+
version: "0.1.102",
|
|
37
37
|
description: "A unified CLI tool for JV-IT TECHS developers to manage Jai1 Framework. Please contact TeamAI for usage instructions.",
|
|
38
38
|
type: "module",
|
|
39
39
|
bin: {
|
|
@@ -88,6 +88,7 @@ var package_default = {
|
|
|
88
88
|
cronstrue: "^2.50.0",
|
|
89
89
|
execa: "^9.6.1",
|
|
90
90
|
"gray-matter": "^4.0.3",
|
|
91
|
+
ignore: "^7.0.5",
|
|
91
92
|
ink: "^5.0.1",
|
|
92
93
|
"ink-spinner": "^5.0.0",
|
|
93
94
|
"ink-text-input": "^6.0.0",
|
|
@@ -3389,9 +3390,9 @@ var IdeDetectionService = class {
|
|
|
3389
3390
|
/**
|
|
3390
3391
|
* Check if a path exists
|
|
3391
3392
|
*/
|
|
3392
|
-
async pathExists(
|
|
3393
|
+
async pathExists(path10) {
|
|
3393
3394
|
try {
|
|
3394
|
-
await fs7.access(
|
|
3395
|
+
await fs7.access(path10);
|
|
3395
3396
|
return true;
|
|
3396
3397
|
} catch {
|
|
3397
3398
|
return false;
|
|
@@ -5113,8 +5114,8 @@ var ChatApp = ({ service, initialModel }) => {
|
|
|
5113
5114
|
|
|
5114
5115
|
// src/server/web-chat-server.ts
|
|
5115
5116
|
import http from "http";
|
|
5116
|
-
import
|
|
5117
|
-
import
|
|
5117
|
+
import fs9 from "fs";
|
|
5118
|
+
import path5 from "path";
|
|
5118
5119
|
import { fileURLToPath } from "url";
|
|
5119
5120
|
|
|
5120
5121
|
// src/server/session.ts
|
|
@@ -5246,9 +5247,363 @@ var SessionManager = class {
|
|
|
5246
5247
|
}
|
|
5247
5248
|
};
|
|
5248
5249
|
|
|
5250
|
+
// src/server/file-service.ts
|
|
5251
|
+
import fs8 from "fs";
|
|
5252
|
+
import path4 from "path";
|
|
5253
|
+
import ignore from "ignore";
|
|
5254
|
+
var ALWAYS_EXCLUDED = [
|
|
5255
|
+
"node_modules",
|
|
5256
|
+
"vendor",
|
|
5257
|
+
".git",
|
|
5258
|
+
".env",
|
|
5259
|
+
".env.*",
|
|
5260
|
+
".env.local",
|
|
5261
|
+
".env.*.local",
|
|
5262
|
+
"dist",
|
|
5263
|
+
"build",
|
|
5264
|
+
".next",
|
|
5265
|
+
".nuxt",
|
|
5266
|
+
".output",
|
|
5267
|
+
".cache",
|
|
5268
|
+
"*.lock",
|
|
5269
|
+
"package-lock.json",
|
|
5270
|
+
"pnpm-lock.yaml",
|
|
5271
|
+
"yarn.lock",
|
|
5272
|
+
"composer.lock",
|
|
5273
|
+
".DS_Store",
|
|
5274
|
+
"Thumbs.db",
|
|
5275
|
+
"*.log",
|
|
5276
|
+
"*.tmp",
|
|
5277
|
+
"*.temp",
|
|
5278
|
+
".idea",
|
|
5279
|
+
".vscode",
|
|
5280
|
+
"*.min.js",
|
|
5281
|
+
"*.min.css",
|
|
5282
|
+
"*.map",
|
|
5283
|
+
"coverage",
|
|
5284
|
+
".nyc_output",
|
|
5285
|
+
"__pycache__",
|
|
5286
|
+
"*.pyc",
|
|
5287
|
+
".pytest_cache",
|
|
5288
|
+
".mypy_cache"
|
|
5289
|
+
];
|
|
5290
|
+
var TEXT_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
5291
|
+
// Web
|
|
5292
|
+
".html",
|
|
5293
|
+
".htm",
|
|
5294
|
+
".css",
|
|
5295
|
+
".scss",
|
|
5296
|
+
".sass",
|
|
5297
|
+
".less",
|
|
5298
|
+
".js",
|
|
5299
|
+
".jsx",
|
|
5300
|
+
".ts",
|
|
5301
|
+
".tsx",
|
|
5302
|
+
".mjs",
|
|
5303
|
+
".cjs",
|
|
5304
|
+
".vue",
|
|
5305
|
+
".svelte",
|
|
5306
|
+
".astro",
|
|
5307
|
+
// Data
|
|
5308
|
+
".json",
|
|
5309
|
+
".yaml",
|
|
5310
|
+
".yml",
|
|
5311
|
+
".toml",
|
|
5312
|
+
".xml",
|
|
5313
|
+
".csv",
|
|
5314
|
+
// Docs
|
|
5315
|
+
".md",
|
|
5316
|
+
".mdx",
|
|
5317
|
+
".txt",
|
|
5318
|
+
".rst",
|
|
5319
|
+
".adoc",
|
|
5320
|
+
// Config
|
|
5321
|
+
".env.example",
|
|
5322
|
+
".gitignore",
|
|
5323
|
+
".dockerignore",
|
|
5324
|
+
".npmrc",
|
|
5325
|
+
".nvmrc",
|
|
5326
|
+
".editorconfig",
|
|
5327
|
+
".prettierrc",
|
|
5328
|
+
".eslintrc",
|
|
5329
|
+
".babelrc",
|
|
5330
|
+
// Server
|
|
5331
|
+
".php",
|
|
5332
|
+
".py",
|
|
5333
|
+
".rb",
|
|
5334
|
+
".go",
|
|
5335
|
+
".java",
|
|
5336
|
+
".kt",
|
|
5337
|
+
".scala",
|
|
5338
|
+
".rs",
|
|
5339
|
+
".swift",
|
|
5340
|
+
".c",
|
|
5341
|
+
".cpp",
|
|
5342
|
+
".h",
|
|
5343
|
+
".hpp",
|
|
5344
|
+
".cs",
|
|
5345
|
+
// Shell
|
|
5346
|
+
".sh",
|
|
5347
|
+
".bash",
|
|
5348
|
+
".zsh",
|
|
5349
|
+
".fish",
|
|
5350
|
+
".ps1",
|
|
5351
|
+
".bat",
|
|
5352
|
+
".cmd",
|
|
5353
|
+
// Database
|
|
5354
|
+
".sql",
|
|
5355
|
+
".prisma",
|
|
5356
|
+
".graphql",
|
|
5357
|
+
".gql",
|
|
5358
|
+
// Other
|
|
5359
|
+
".dockerfile",
|
|
5360
|
+
".makefile",
|
|
5361
|
+
".rake",
|
|
5362
|
+
".gradle"
|
|
5363
|
+
]);
|
|
5364
|
+
var TEXT_FILES = /* @__PURE__ */ new Set([
|
|
5365
|
+
"Makefile",
|
|
5366
|
+
"Dockerfile",
|
|
5367
|
+
"Vagrantfile",
|
|
5368
|
+
"Gemfile",
|
|
5369
|
+
"Rakefile",
|
|
5370
|
+
"Procfile",
|
|
5371
|
+
"Brewfile",
|
|
5372
|
+
".gitignore",
|
|
5373
|
+
".dockerignore",
|
|
5374
|
+
".npmrc",
|
|
5375
|
+
".nvmrc",
|
|
5376
|
+
".env.example",
|
|
5377
|
+
"LICENSE",
|
|
5378
|
+
"README",
|
|
5379
|
+
"CHANGELOG",
|
|
5380
|
+
"CONTRIBUTING",
|
|
5381
|
+
"AUTHORS",
|
|
5382
|
+
"CODEOWNERS"
|
|
5383
|
+
]);
|
|
5384
|
+
function getLanguage(filePath) {
|
|
5385
|
+
const ext = path4.extname(filePath).toLowerCase();
|
|
5386
|
+
const langMap = {
|
|
5387
|
+
".ts": "typescript",
|
|
5388
|
+
".tsx": "typescript",
|
|
5389
|
+
".js": "javascript",
|
|
5390
|
+
".jsx": "javascript",
|
|
5391
|
+
".mjs": "javascript",
|
|
5392
|
+
".cjs": "javascript",
|
|
5393
|
+
".py": "python",
|
|
5394
|
+
".rb": "ruby",
|
|
5395
|
+
".php": "php",
|
|
5396
|
+
".java": "java",
|
|
5397
|
+
".kt": "kotlin",
|
|
5398
|
+
".go": "go",
|
|
5399
|
+
".rs": "rust",
|
|
5400
|
+
".swift": "swift",
|
|
5401
|
+
".c": "c",
|
|
5402
|
+
".h": "c",
|
|
5403
|
+
".cpp": "cpp",
|
|
5404
|
+
".hpp": "cpp",
|
|
5405
|
+
".cs": "csharp",
|
|
5406
|
+
".css": "css",
|
|
5407
|
+
".scss": "scss",
|
|
5408
|
+
".sass": "sass",
|
|
5409
|
+
".less": "less",
|
|
5410
|
+
".html": "html",
|
|
5411
|
+
".htm": "html",
|
|
5412
|
+
".vue": "vue",
|
|
5413
|
+
".svelte": "svelte",
|
|
5414
|
+
".json": "json",
|
|
5415
|
+
".yaml": "yaml",
|
|
5416
|
+
".yml": "yaml",
|
|
5417
|
+
".xml": "xml",
|
|
5418
|
+
".md": "markdown",
|
|
5419
|
+
".mdx": "markdown",
|
|
5420
|
+
".sql": "sql",
|
|
5421
|
+
".graphql": "graphql",
|
|
5422
|
+
".gql": "graphql",
|
|
5423
|
+
".sh": "bash",
|
|
5424
|
+
".bash": "bash",
|
|
5425
|
+
".zsh": "bash",
|
|
5426
|
+
".dockerfile": "dockerfile",
|
|
5427
|
+
".prisma": "prisma"
|
|
5428
|
+
};
|
|
5429
|
+
return langMap[ext] || "text";
|
|
5430
|
+
}
|
|
5431
|
+
function isTextFile(filePath) {
|
|
5432
|
+
const ext = path4.extname(filePath).toLowerCase();
|
|
5433
|
+
const name = path4.basename(filePath);
|
|
5434
|
+
if (TEXT_FILES.has(name)) return true;
|
|
5435
|
+
if (ext && TEXT_EXTENSIONS.has(ext)) return true;
|
|
5436
|
+
if (name.endsWith("rc") || name.endsWith("config")) return true;
|
|
5437
|
+
return false;
|
|
5438
|
+
}
|
|
5439
|
+
var FileService = class {
|
|
5440
|
+
workingDir;
|
|
5441
|
+
maxFileSize;
|
|
5442
|
+
maxFilesPerRequest;
|
|
5443
|
+
maxSearchResults;
|
|
5444
|
+
ignoreFilter;
|
|
5445
|
+
constructor(options) {
|
|
5446
|
+
this.workingDir = path4.resolve(options.workingDir);
|
|
5447
|
+
this.maxFileSize = options.maxFileSize ?? 1024 * 1024;
|
|
5448
|
+
this.maxFilesPerRequest = options.maxFilesPerRequest ?? 5;
|
|
5449
|
+
this.maxSearchResults = options.maxSearchResults ?? 20;
|
|
5450
|
+
this.ignoreFilter = ignore();
|
|
5451
|
+
this.loadIgnorePatterns();
|
|
5452
|
+
}
|
|
5453
|
+
/**
|
|
5454
|
+
* Load ignore patterns from .gitignore and add always-excluded patterns
|
|
5455
|
+
*/
|
|
5456
|
+
loadIgnorePatterns() {
|
|
5457
|
+
this.ignoreFilter.add(ALWAYS_EXCLUDED);
|
|
5458
|
+
const gitignorePath = path4.join(this.workingDir, ".gitignore");
|
|
5459
|
+
try {
|
|
5460
|
+
if (fs8.existsSync(gitignorePath)) {
|
|
5461
|
+
const content = fs8.readFileSync(gitignorePath, "utf-8");
|
|
5462
|
+
this.ignoreFilter.add(content);
|
|
5463
|
+
}
|
|
5464
|
+
} catch {
|
|
5465
|
+
}
|
|
5466
|
+
}
|
|
5467
|
+
/**
|
|
5468
|
+
* Check if a path should be ignored
|
|
5469
|
+
*/
|
|
5470
|
+
isIgnored(relativePath) {
|
|
5471
|
+
const normalizedPath = relativePath.replace(/\\/g, "/");
|
|
5472
|
+
return this.ignoreFilter.ignores(normalizedPath);
|
|
5473
|
+
}
|
|
5474
|
+
/**
|
|
5475
|
+
* Validate path is within working directory (prevent path traversal)
|
|
5476
|
+
*/
|
|
5477
|
+
validatePath(relativePath) {
|
|
5478
|
+
const cleanPath = relativePath.replace(/^\/+/, "");
|
|
5479
|
+
const absolutePath = path4.resolve(this.workingDir, cleanPath);
|
|
5480
|
+
if (!absolutePath.startsWith(this.workingDir + path4.sep) && absolutePath !== this.workingDir) {
|
|
5481
|
+
throw new Error("Path traversal detected");
|
|
5482
|
+
}
|
|
5483
|
+
return absolutePath;
|
|
5484
|
+
}
|
|
5485
|
+
/**
|
|
5486
|
+
* Get relative path from working directory
|
|
5487
|
+
*/
|
|
5488
|
+
getRelativePath(absolutePath) {
|
|
5489
|
+
return path4.relative(this.workingDir, absolutePath);
|
|
5490
|
+
}
|
|
5491
|
+
/**
|
|
5492
|
+
* Search files matching a query (for autocomplete)
|
|
5493
|
+
* Returns files where path contains the query string
|
|
5494
|
+
*/
|
|
5495
|
+
async searchFiles(query, limit) {
|
|
5496
|
+
const maxResults = Math.min(limit ?? this.maxSearchResults, 50);
|
|
5497
|
+
const results = [];
|
|
5498
|
+
const normalizedQuery = query.toLowerCase().replace(/\\/g, "/");
|
|
5499
|
+
const searchDir = async (dirPath, depth = 0) => {
|
|
5500
|
+
if (depth > 10 || results.length >= maxResults) return;
|
|
5501
|
+
try {
|
|
5502
|
+
const entries = await fs8.promises.readdir(dirPath, { withFileTypes: true });
|
|
5503
|
+
for (const entry of entries) {
|
|
5504
|
+
if (results.length >= maxResults) break;
|
|
5505
|
+
const entryPath = path4.join(dirPath, entry.name);
|
|
5506
|
+
const relativePath = this.getRelativePath(entryPath);
|
|
5507
|
+
if (this.isIgnored(relativePath)) continue;
|
|
5508
|
+
const normalizedRelPath = relativePath.toLowerCase().replace(/\\/g, "/");
|
|
5509
|
+
if (normalizedRelPath.includes(normalizedQuery)) {
|
|
5510
|
+
if (entry.isFile() && isTextFile(entry.name)) {
|
|
5511
|
+
try {
|
|
5512
|
+
const stat = await fs8.promises.stat(entryPath);
|
|
5513
|
+
results.push({
|
|
5514
|
+
path: relativePath,
|
|
5515
|
+
name: entry.name,
|
|
5516
|
+
size: stat.size,
|
|
5517
|
+
type: "file",
|
|
5518
|
+
extension: path4.extname(entry.name).slice(1) || void 0
|
|
5519
|
+
});
|
|
5520
|
+
} catch {
|
|
5521
|
+
}
|
|
5522
|
+
}
|
|
5523
|
+
}
|
|
5524
|
+
if (entry.isDirectory()) {
|
|
5525
|
+
await searchDir(entryPath, depth + 1);
|
|
5526
|
+
}
|
|
5527
|
+
}
|
|
5528
|
+
} catch {
|
|
5529
|
+
}
|
|
5530
|
+
};
|
|
5531
|
+
await searchDir(this.workingDir);
|
|
5532
|
+
results.sort((a, b) => {
|
|
5533
|
+
if (a.path.length !== b.path.length) {
|
|
5534
|
+
return a.path.length - b.path.length;
|
|
5535
|
+
}
|
|
5536
|
+
return a.path.localeCompare(b.path);
|
|
5537
|
+
});
|
|
5538
|
+
return results;
|
|
5539
|
+
}
|
|
5540
|
+
/**
|
|
5541
|
+
* Read a single file
|
|
5542
|
+
*/
|
|
5543
|
+
async readFile(relativePath) {
|
|
5544
|
+
const absolutePath = this.validatePath(relativePath);
|
|
5545
|
+
const normalizedRelPath = this.getRelativePath(absolutePath);
|
|
5546
|
+
if (this.isIgnored(normalizedRelPath)) {
|
|
5547
|
+
throw new Error("File is excluded by ignore rules");
|
|
5548
|
+
}
|
|
5549
|
+
if (!isTextFile(absolutePath)) {
|
|
5550
|
+
throw new Error("Only text files can be read");
|
|
5551
|
+
}
|
|
5552
|
+
const stat = await fs8.promises.stat(absolutePath);
|
|
5553
|
+
if (!stat.isFile()) {
|
|
5554
|
+
throw new Error("Path is not a file");
|
|
5555
|
+
}
|
|
5556
|
+
if (stat.size > this.maxFileSize) {
|
|
5557
|
+
throw new Error(`File exceeds maximum size of ${Math.round(this.maxFileSize / 1024)}KB`);
|
|
5558
|
+
}
|
|
5559
|
+
const content = await fs8.promises.readFile(absolutePath, "utf-8");
|
|
5560
|
+
return {
|
|
5561
|
+
path: normalizedRelPath,
|
|
5562
|
+
content,
|
|
5563
|
+
size: stat.size,
|
|
5564
|
+
language: getLanguage(absolutePath)
|
|
5565
|
+
};
|
|
5566
|
+
}
|
|
5567
|
+
/**
|
|
5568
|
+
* Read multiple files (batch)
|
|
5569
|
+
*/
|
|
5570
|
+
async readFiles(relativePaths) {
|
|
5571
|
+
const paths = relativePaths.slice(0, this.maxFilesPerRequest);
|
|
5572
|
+
const results = await Promise.all(
|
|
5573
|
+
paths.map(async (filePath) => {
|
|
5574
|
+
try {
|
|
5575
|
+
const content = await this.readFile(filePath);
|
|
5576
|
+
return content;
|
|
5577
|
+
} catch (error) {
|
|
5578
|
+
return {
|
|
5579
|
+
path: filePath,
|
|
5580
|
+
content: "",
|
|
5581
|
+
size: 0,
|
|
5582
|
+
language: "text",
|
|
5583
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
5584
|
+
};
|
|
5585
|
+
}
|
|
5586
|
+
})
|
|
5587
|
+
);
|
|
5588
|
+
return results;
|
|
5589
|
+
}
|
|
5590
|
+
/**
|
|
5591
|
+
* Get working directory info
|
|
5592
|
+
*/
|
|
5593
|
+
getWorkingDir() {
|
|
5594
|
+
return this.workingDir;
|
|
5595
|
+
}
|
|
5596
|
+
/**
|
|
5597
|
+
* Get max files per request limit
|
|
5598
|
+
*/
|
|
5599
|
+
getMaxFilesLimit() {
|
|
5600
|
+
return this.maxFilesPerRequest;
|
|
5601
|
+
}
|
|
5602
|
+
};
|
|
5603
|
+
|
|
5249
5604
|
// src/server/web-chat-server.ts
|
|
5250
5605
|
var __filename = fileURLToPath(import.meta.url);
|
|
5251
|
-
var __dirname =
|
|
5606
|
+
var __dirname = path5.dirname(__filename);
|
|
5252
5607
|
var MIME_TYPES = {
|
|
5253
5608
|
".html": "text/html",
|
|
5254
5609
|
".css": "text/css",
|
|
@@ -5322,22 +5677,23 @@ function createWebChatServer(options) {
|
|
|
5322
5677
|
const sessionManager = new SessionManager();
|
|
5323
5678
|
let server = null;
|
|
5324
5679
|
let session = null;
|
|
5325
|
-
const
|
|
5680
|
+
const fileService = new FileService({ workingDir: process.cwd() });
|
|
5681
|
+
const staticDir = path5.join(__dirname, "web-chat");
|
|
5326
5682
|
async function serveStaticFile(res, filePath) {
|
|
5327
|
-
const fullPath =
|
|
5683
|
+
const fullPath = path5.join(staticDir, filePath);
|
|
5328
5684
|
if (!fullPath.startsWith(staticDir)) {
|
|
5329
5685
|
sendError(res, 403, "ERR-WC-006", "Forbidden");
|
|
5330
5686
|
return;
|
|
5331
5687
|
}
|
|
5332
5688
|
try {
|
|
5333
|
-
const stat = await
|
|
5689
|
+
const stat = await fs9.promises.stat(fullPath);
|
|
5334
5690
|
if (!stat.isFile()) {
|
|
5335
5691
|
sendError(res, 404, "ERR-WC-007", "Not found");
|
|
5336
5692
|
return;
|
|
5337
5693
|
}
|
|
5338
|
-
const ext =
|
|
5694
|
+
const ext = path5.extname(fullPath);
|
|
5339
5695
|
const contentType = MIME_TYPES[ext] || "application/octet-stream";
|
|
5340
|
-
const content = await
|
|
5696
|
+
const content = await fs9.promises.readFile(fullPath);
|
|
5341
5697
|
res.writeHead(200, { "Content-Type": contentType });
|
|
5342
5698
|
res.end(content);
|
|
5343
5699
|
} catch (error) {
|
|
@@ -5440,6 +5796,83 @@ function createWebChatServer(options) {
|
|
|
5440
5796
|
}
|
|
5441
5797
|
return;
|
|
5442
5798
|
}
|
|
5799
|
+
if (pathname === "/api/files/search" && req.method === "GET") {
|
|
5800
|
+
try {
|
|
5801
|
+
const url = new URL(req.url || "/", `http://${req.headers.host}`);
|
|
5802
|
+
const query = url.searchParams.get("q") || "";
|
|
5803
|
+
const limit = parseInt(url.searchParams.get("limit") || "10", 10);
|
|
5804
|
+
if (!query || query.length < 1) {
|
|
5805
|
+
sendJson(res, 200, { success: true, data: { files: [] } });
|
|
5806
|
+
return;
|
|
5807
|
+
}
|
|
5808
|
+
const files = await fileService.searchFiles(query, Math.min(limit, 20));
|
|
5809
|
+
sendJson(res, 200, {
|
|
5810
|
+
success: true,
|
|
5811
|
+
data: { files }
|
|
5812
|
+
});
|
|
5813
|
+
} catch (error) {
|
|
5814
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
5815
|
+
sendError(res, 500, "ERR-WC-011", `File search failed: ${errorMessage}`);
|
|
5816
|
+
}
|
|
5817
|
+
return;
|
|
5818
|
+
}
|
|
5819
|
+
if (pathname === "/api/files/read" && req.method === "GET") {
|
|
5820
|
+
try {
|
|
5821
|
+
const url = new URL(req.url || "/", `http://${req.headers.host}`);
|
|
5822
|
+
const filePath = url.searchParams.get("path");
|
|
5823
|
+
if (!filePath) {
|
|
5824
|
+
sendError(res, 400, "ERR-WC-012", "Missing file path");
|
|
5825
|
+
return;
|
|
5826
|
+
}
|
|
5827
|
+
const fileContent = await fileService.readFile(filePath);
|
|
5828
|
+
sendJson(res, 200, {
|
|
5829
|
+
success: true,
|
|
5830
|
+
data: fileContent
|
|
5831
|
+
});
|
|
5832
|
+
} catch (error) {
|
|
5833
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
5834
|
+
if (errorMessage.includes("Path traversal")) {
|
|
5835
|
+
sendError(res, 403, "ERR-WC-013", "Access denied");
|
|
5836
|
+
} else if (errorMessage.includes("ENOENT")) {
|
|
5837
|
+
sendError(res, 404, "ERR-WC-014", "File not found");
|
|
5838
|
+
} else {
|
|
5839
|
+
sendError(res, 400, "ERR-WC-015", errorMessage);
|
|
5840
|
+
}
|
|
5841
|
+
}
|
|
5842
|
+
return;
|
|
5843
|
+
}
|
|
5844
|
+
if (pathname === "/api/files/batch" && req.method === "POST") {
|
|
5845
|
+
try {
|
|
5846
|
+
const body = await parseJsonBody(req);
|
|
5847
|
+
if (!body.paths || !Array.isArray(body.paths) || body.paths.length === 0) {
|
|
5848
|
+
sendError(res, 400, "ERR-WC-016", "Missing or invalid paths array");
|
|
5849
|
+
return;
|
|
5850
|
+
}
|
|
5851
|
+
if (body.paths.length > fileService.getMaxFilesLimit()) {
|
|
5852
|
+
sendError(res, 400, "ERR-WC-017", `Maximum ${fileService.getMaxFilesLimit()} files allowed per request`);
|
|
5853
|
+
return;
|
|
5854
|
+
}
|
|
5855
|
+
const files = await fileService.readFiles(body.paths);
|
|
5856
|
+
sendJson(res, 200, {
|
|
5857
|
+
success: true,
|
|
5858
|
+
data: { files }
|
|
5859
|
+
});
|
|
5860
|
+
} catch (error) {
|
|
5861
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
5862
|
+
sendError(res, 500, "ERR-WC-018", `Batch read failed: ${errorMessage}`);
|
|
5863
|
+
}
|
|
5864
|
+
return;
|
|
5865
|
+
}
|
|
5866
|
+
if (pathname === "/api/files/info" && req.method === "GET") {
|
|
5867
|
+
sendJson(res, 200, {
|
|
5868
|
+
success: true,
|
|
5869
|
+
data: {
|
|
5870
|
+
workingDir: fileService.getWorkingDir(),
|
|
5871
|
+
maxFiles: fileService.getMaxFilesLimit()
|
|
5872
|
+
}
|
|
5873
|
+
});
|
|
5874
|
+
return;
|
|
5875
|
+
}
|
|
5443
5876
|
if (pathname === "/api/chat" && req.method === "POST") {
|
|
5444
5877
|
try {
|
|
5445
5878
|
const body = await parseJsonBody(req);
|
|
@@ -5793,8 +6226,8 @@ function createStatsCommand() {
|
|
|
5793
6226
|
import { Command as Command15 } from "commander";
|
|
5794
6227
|
|
|
5795
6228
|
// src/services/translation.service.ts
|
|
5796
|
-
import { promises as
|
|
5797
|
-
import
|
|
6229
|
+
import { promises as fs10 } from "fs";
|
|
6230
|
+
import path6 from "path";
|
|
5798
6231
|
import pLimit from "p-limit";
|
|
5799
6232
|
import pRetry from "p-retry";
|
|
5800
6233
|
var TRANSLATABLE_EXTS = [".md", ".txt", ".json", ".yaml", ".yml"];
|
|
@@ -5812,7 +6245,7 @@ var TranslationService = class {
|
|
|
5812
6245
|
*/
|
|
5813
6246
|
async detectInputType(input5) {
|
|
5814
6247
|
try {
|
|
5815
|
-
const stat = await
|
|
6248
|
+
const stat = await fs10.stat(input5);
|
|
5816
6249
|
if (stat.isDirectory()) return "folder";
|
|
5817
6250
|
if (stat.isFile()) return "file";
|
|
5818
6251
|
} catch {
|
|
@@ -5849,13 +6282,13 @@ var TranslationService = class {
|
|
|
5849
6282
|
*/
|
|
5850
6283
|
async translateFile(filePath) {
|
|
5851
6284
|
try {
|
|
5852
|
-
const content = await
|
|
5853
|
-
const ext =
|
|
6285
|
+
const content = await fs10.readFile(filePath, "utf-8");
|
|
6286
|
+
const ext = path6.extname(filePath).toLowerCase();
|
|
5854
6287
|
const fileType = this.getFileType(ext);
|
|
5855
6288
|
const translatedContent = await this.translateWithRetry(content, fileType);
|
|
5856
6289
|
const outputPath = this.generateOutputPath(filePath);
|
|
5857
6290
|
if (!this.options.dryRun) {
|
|
5858
|
-
await
|
|
6291
|
+
await fs10.writeFile(outputPath, translatedContent, "utf-8");
|
|
5859
6292
|
}
|
|
5860
6293
|
return {
|
|
5861
6294
|
inputPath: filePath,
|
|
@@ -5910,27 +6343,27 @@ var TranslationService = class {
|
|
|
5910
6343
|
if (this.options.output) {
|
|
5911
6344
|
return this.options.output;
|
|
5912
6345
|
}
|
|
5913
|
-
const ext =
|
|
5914
|
-
const base =
|
|
5915
|
-
const dir =
|
|
6346
|
+
const ext = path6.extname(inputPath);
|
|
6347
|
+
const base = path6.basename(inputPath, ext);
|
|
6348
|
+
const dir = path6.dirname(inputPath);
|
|
5916
6349
|
const lang = this.options.to;
|
|
5917
|
-
return
|
|
6350
|
+
return path6.join(dir, `${base}.${lang}${ext}`);
|
|
5918
6351
|
}
|
|
5919
6352
|
/**
|
|
5920
6353
|
* Discover translatable files in folder recursively
|
|
5921
6354
|
*/
|
|
5922
6355
|
async discoverFiles(folderPath) {
|
|
5923
|
-
const entries = await
|
|
6356
|
+
const entries = await fs10.readdir(folderPath, { withFileTypes: true });
|
|
5924
6357
|
const files = [];
|
|
5925
6358
|
for (const entry of entries) {
|
|
5926
|
-
const fullPath =
|
|
6359
|
+
const fullPath = path6.join(folderPath, entry.name);
|
|
5927
6360
|
if (entry.isDirectory()) {
|
|
5928
6361
|
if (!entry.name.startsWith(".") && entry.name !== "node_modules") {
|
|
5929
6362
|
const subFiles = await this.discoverFiles(fullPath);
|
|
5930
6363
|
files.push(...subFiles);
|
|
5931
6364
|
}
|
|
5932
6365
|
} else if (entry.isFile()) {
|
|
5933
|
-
const ext =
|
|
6366
|
+
const ext = path6.extname(entry.name).toLowerCase();
|
|
5934
6367
|
if (TRANSLATABLE_EXTS.includes(ext)) {
|
|
5935
6368
|
if (!this.isTranslatedFile(entry.name)) {
|
|
5936
6369
|
files.push(fullPath);
|
|
@@ -6432,7 +6865,7 @@ function createImageCommand() {
|
|
|
6432
6865
|
import { Command as Command21 } from "commander";
|
|
6433
6866
|
import { select as select2, input, confirm as confirm5 } from "@inquirer/prompts";
|
|
6434
6867
|
import os from "os";
|
|
6435
|
-
import { promises as
|
|
6868
|
+
import { promises as fs11 } from "fs";
|
|
6436
6869
|
import { join as join5 } from "path";
|
|
6437
6870
|
async function collectContext() {
|
|
6438
6871
|
const context = {
|
|
@@ -6440,12 +6873,12 @@ async function collectContext() {
|
|
|
6440
6873
|
};
|
|
6441
6874
|
try {
|
|
6442
6875
|
const packageJsonPath = new URL("../../package.json", import.meta.url);
|
|
6443
|
-
const packageJson = JSON.parse(await
|
|
6876
|
+
const packageJson = JSON.parse(await fs11.readFile(packageJsonPath, "utf-8"));
|
|
6444
6877
|
context.cli_version = packageJson.version;
|
|
6445
6878
|
} catch {
|
|
6446
6879
|
}
|
|
6447
6880
|
try {
|
|
6448
|
-
const projectPackageJson = await
|
|
6881
|
+
const projectPackageJson = await fs11.readFile(join5(process.cwd(), "package.json"), "utf-8");
|
|
6449
6882
|
const projectData = JSON.parse(projectPackageJson);
|
|
6450
6883
|
context.project_name = projectData.name;
|
|
6451
6884
|
} catch {
|
|
@@ -6516,11 +6949,7 @@ async function handleInteractiveFeedback(config) {
|
|
|
6516
6949
|
return true;
|
|
6517
6950
|
}
|
|
6518
6951
|
});
|
|
6519
|
-
const
|
|
6520
|
-
message: "Include system context (OS, CLI version)?",
|
|
6521
|
-
default: true
|
|
6522
|
-
});
|
|
6523
|
-
const context = includeContext ? await collectContext() : void 0;
|
|
6952
|
+
const context = await collectContext();
|
|
6524
6953
|
console.log("\n\u{1F4CB} Summary:\n");
|
|
6525
6954
|
console.log(`Type: ${type}`);
|
|
6526
6955
|
console.log(`Title: ${title}`);
|
|
@@ -8701,8 +9130,8 @@ var HttpView = () => {
|
|
|
8701
9130
|
import React37, { useState as useState24 } from "react";
|
|
8702
9131
|
import { Box as Box27, Text as Text28, useInput as useInput22 } from "ink";
|
|
8703
9132
|
import TextInput14 from "ink-text-input";
|
|
8704
|
-
import * as
|
|
8705
|
-
import * as
|
|
9133
|
+
import * as fs12 from "fs";
|
|
9134
|
+
import * as path7 from "path";
|
|
8706
9135
|
var MarkdownView = () => {
|
|
8707
9136
|
const [filePath, setFilePath] = useState24("");
|
|
8708
9137
|
const [content, setContent] = useState24("");
|
|
@@ -8722,12 +9151,12 @@ var MarkdownView = () => {
|
|
|
8722
9151
|
}
|
|
8723
9152
|
try {
|
|
8724
9153
|
setError("");
|
|
8725
|
-
const resolvedPath =
|
|
8726
|
-
if (!
|
|
9154
|
+
const resolvedPath = path7.resolve(filePath);
|
|
9155
|
+
if (!fs12.existsSync(resolvedPath)) {
|
|
8727
9156
|
setError(`File not found: ${resolvedPath}`);
|
|
8728
9157
|
return;
|
|
8729
9158
|
}
|
|
8730
|
-
let fileContent =
|
|
9159
|
+
let fileContent = fs12.readFileSync(resolvedPath, "utf-8");
|
|
8731
9160
|
fileContent = fileContent.replace(/```mermaid\n([\s\S]*?)```/g, (match, code) => {
|
|
8732
9161
|
return `
|
|
8733
9162
|
\u{1F3A8} **Mermaid Diagram**
|
|
@@ -9008,8 +9437,8 @@ import { Command as Command36 } from "commander";
|
|
|
9008
9437
|
import { checkbox as checkbox3, confirm as confirm6 } from "@inquirer/prompts";
|
|
9009
9438
|
|
|
9010
9439
|
// src/services/deps.service.ts
|
|
9011
|
-
import { promises as
|
|
9012
|
-
import
|
|
9440
|
+
import { promises as fs13 } from "fs";
|
|
9441
|
+
import path8 from "path";
|
|
9013
9442
|
import { execSync } from "child_process";
|
|
9014
9443
|
import pLimit2 from "p-limit";
|
|
9015
9444
|
var DepsService = class {
|
|
@@ -9018,9 +9447,9 @@ var DepsService = class {
|
|
|
9018
9447
|
* Đọc package.json từ thư mục
|
|
9019
9448
|
*/
|
|
9020
9449
|
async readPackageJson(cwd) {
|
|
9021
|
-
const pkgPath =
|
|
9450
|
+
const pkgPath = path8.join(cwd, "package.json");
|
|
9022
9451
|
try {
|
|
9023
|
-
const content = await
|
|
9452
|
+
const content = await fs13.readFile(pkgPath, "utf-8");
|
|
9024
9453
|
return JSON.parse(content);
|
|
9025
9454
|
} catch (error) {
|
|
9026
9455
|
if (error.code === "ENOENT") {
|
|
@@ -9126,7 +9555,7 @@ var DepsService = class {
|
|
|
9126
9555
|
];
|
|
9127
9556
|
for (const { file, pm } of lockFiles) {
|
|
9128
9557
|
try {
|
|
9129
|
-
await
|
|
9558
|
+
await fs13.access(path8.join(cwd, file));
|
|
9130
9559
|
return pm;
|
|
9131
9560
|
} catch {
|
|
9132
9561
|
}
|
|
@@ -9410,7 +9839,7 @@ import { Command as Command41 } from "commander";
|
|
|
9410
9839
|
import { Command as Command38 } from "commander";
|
|
9411
9840
|
|
|
9412
9841
|
// src/services/starter-kit.service.ts
|
|
9413
|
-
import { promises as
|
|
9842
|
+
import { promises as fs14 } from "fs";
|
|
9414
9843
|
import { join as join6 } from "path";
|
|
9415
9844
|
import AdmZip from "adm-zip";
|
|
9416
9845
|
var StarterKitService = class {
|
|
@@ -9459,16 +9888,16 @@ var StarterKitService = class {
|
|
|
9459
9888
|
}
|
|
9460
9889
|
if (onProgress) onProgress(30);
|
|
9461
9890
|
const tmpDir = join6(process.env.TMPDIR || "/tmp", "jai1-kits");
|
|
9462
|
-
await
|
|
9891
|
+
await fs14.mkdir(tmpDir, { recursive: true });
|
|
9463
9892
|
const tmpFile = join6(tmpDir, `${slug}.zip`);
|
|
9464
9893
|
const buffer = await response.arrayBuffer();
|
|
9465
|
-
await
|
|
9894
|
+
await fs14.writeFile(tmpFile, Buffer.from(buffer));
|
|
9466
9895
|
if (onProgress) onProgress(60);
|
|
9467
9896
|
const zip = new AdmZip(tmpFile);
|
|
9468
|
-
await
|
|
9897
|
+
await fs14.mkdir(targetDir, { recursive: true });
|
|
9469
9898
|
zip.extractAllTo(targetDir, true);
|
|
9470
9899
|
if (onProgress) onProgress(100);
|
|
9471
|
-
await
|
|
9900
|
+
await fs14.unlink(tmpFile);
|
|
9472
9901
|
}
|
|
9473
9902
|
};
|
|
9474
9903
|
|
|
@@ -9563,7 +9992,7 @@ Post-Init Commands:`);
|
|
|
9563
9992
|
|
|
9564
9993
|
// src/commands/kit/create.ts
|
|
9565
9994
|
import { Command as Command40 } from "commander";
|
|
9566
|
-
import { promises as
|
|
9995
|
+
import { promises as fs15 } from "fs";
|
|
9567
9996
|
import { join as join7 } from "path";
|
|
9568
9997
|
import { select as select3, input as input2, checkbox as checkbox4 } from "@inquirer/prompts";
|
|
9569
9998
|
import { execa as execa2 } from "execa";
|
|
@@ -9629,7 +10058,7 @@ function createKitCreateCommand() {
|
|
|
9629
10058
|
}
|
|
9630
10059
|
const targetDir = directory || join7(process.cwd(), options.name || slug);
|
|
9631
10060
|
try {
|
|
9632
|
-
await
|
|
10061
|
+
await fs15.access(targetDir);
|
|
9633
10062
|
throw new Error(`Directory already exists: ${targetDir}`);
|
|
9634
10063
|
} catch (error) {
|
|
9635
10064
|
if (error.code !== "ENOENT") {
|
|
@@ -9747,7 +10176,7 @@ function createKitCreateCommand() {
|
|
|
9747
10176
|
async function applyVariableSubstitution(dir, variables) {
|
|
9748
10177
|
const files = await getAllFiles(dir);
|
|
9749
10178
|
for (const file of files) {
|
|
9750
|
-
let content = await
|
|
10179
|
+
let content = await fs15.readFile(file, "utf-8");
|
|
9751
10180
|
let modified = false;
|
|
9752
10181
|
for (const [key, value] of Object.entries(variables)) {
|
|
9753
10182
|
const regex = new RegExp(`\\{\\{${key}\\}\\}`, "g");
|
|
@@ -9757,13 +10186,13 @@ async function applyVariableSubstitution(dir, variables) {
|
|
|
9757
10186
|
}
|
|
9758
10187
|
}
|
|
9759
10188
|
if (modified) {
|
|
9760
|
-
await
|
|
10189
|
+
await fs15.writeFile(file, content, "utf-8");
|
|
9761
10190
|
}
|
|
9762
10191
|
}
|
|
9763
10192
|
}
|
|
9764
10193
|
async function getAllFiles(dir) {
|
|
9765
10194
|
const files = [];
|
|
9766
|
-
const entries = await
|
|
10195
|
+
const entries = await fs15.readdir(dir, { withFileTypes: true });
|
|
9767
10196
|
for (const entry of entries) {
|
|
9768
10197
|
const fullPath = join7(dir, entry.name);
|
|
9769
10198
|
if (entry.isDirectory()) {
|
|
@@ -9850,7 +10279,7 @@ function createRulesListCommand() {
|
|
|
9850
10279
|
|
|
9851
10280
|
// src/commands/rules/init.ts
|
|
9852
10281
|
import { Command as Command43 } from "commander";
|
|
9853
|
-
import { promises as
|
|
10282
|
+
import { promises as fs16 } from "fs";
|
|
9854
10283
|
import { join as join8 } from "path";
|
|
9855
10284
|
import { select as select4, confirm as confirm7 } from "@inquirer/prompts";
|
|
9856
10285
|
function createRulesInitCommand() {
|
|
@@ -9936,7 +10365,7 @@ function createRulesInitCommand() {
|
|
|
9936
10365
|
appliedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
9937
10366
|
customContext: "09-custom.mdc"
|
|
9938
10367
|
};
|
|
9939
|
-
await
|
|
10368
|
+
await fs16.writeFile("jai1-rules.json", JSON.stringify(projectConfig, null, 2));
|
|
9940
10369
|
console.log("\u2713 Created jai1-rules.json");
|
|
9941
10370
|
console.log("\n\u2705 Preset applied successfully!\n");
|
|
9942
10371
|
console.log("Next steps:");
|
|
@@ -9947,10 +10376,10 @@ function createRulesInitCommand() {
|
|
|
9947
10376
|
}
|
|
9948
10377
|
async function applyCursorFormat(bundle) {
|
|
9949
10378
|
const rulesDir = join8(process.cwd(), ".cursor", "rules");
|
|
9950
|
-
await
|
|
10379
|
+
await fs16.mkdir(rulesDir, { recursive: true });
|
|
9951
10380
|
for (const [filename, content] of Object.entries(bundle.files)) {
|
|
9952
10381
|
const filePath = join8(rulesDir, filename);
|
|
9953
|
-
await
|
|
10382
|
+
await fs16.writeFile(filePath, content, "utf-8");
|
|
9954
10383
|
console.log(`\u2713 Created .cursor/rules/${filename}`);
|
|
9955
10384
|
}
|
|
9956
10385
|
}
|
|
@@ -9977,13 +10406,13 @@ async function applyAgentsMdFormat(bundle) {
|
|
|
9977
10406
|
}
|
|
9978
10407
|
}
|
|
9979
10408
|
const agentsMd = sections.join("\n");
|
|
9980
|
-
await
|
|
10409
|
+
await fs16.writeFile("AGENTS.md", agentsMd, "utf-8");
|
|
9981
10410
|
console.log("\u2713 Created AGENTS.md");
|
|
9982
10411
|
}
|
|
9983
10412
|
|
|
9984
10413
|
// src/commands/rules/apply.ts
|
|
9985
10414
|
import { Command as Command44 } from "commander";
|
|
9986
|
-
import { promises as
|
|
10415
|
+
import { promises as fs18 } from "fs";
|
|
9987
10416
|
import { join as join10 } from "path";
|
|
9988
10417
|
import { select as select5, confirm as confirm8, checkbox as checkbox5 } from "@inquirer/prompts";
|
|
9989
10418
|
|
|
@@ -10344,7 +10773,7 @@ Follow all instructions and patterns defined in AGENTS.md above.
|
|
|
10344
10773
|
};
|
|
10345
10774
|
|
|
10346
10775
|
// src/services/backup.service.ts
|
|
10347
|
-
import { promises as
|
|
10776
|
+
import { promises as fs17 } from "fs";
|
|
10348
10777
|
import { join as join9, dirname } from "path";
|
|
10349
10778
|
var BackupService = class {
|
|
10350
10779
|
backupDir = ".jai1/backups";
|
|
@@ -10375,16 +10804,16 @@ var BackupService = class {
|
|
|
10375
10804
|
await this.backupSingleFile("GEMINI.md", backupPath, ideId, backedUpFiles);
|
|
10376
10805
|
hasContent = true;
|
|
10377
10806
|
} else {
|
|
10378
|
-
const stats = await
|
|
10807
|
+
const stats = await fs17.stat(rulesPath);
|
|
10379
10808
|
if (stats.isDirectory()) {
|
|
10380
|
-
const files = await
|
|
10809
|
+
const files = await fs17.readdir(rulesPath);
|
|
10381
10810
|
for (const file of files) {
|
|
10382
10811
|
if (file.endsWith(format.fileExtension)) {
|
|
10383
10812
|
const originalPath = join9(rulesPath, file);
|
|
10384
10813
|
const relativePath = join9(format.rulesPath, file);
|
|
10385
10814
|
const destPath = join9(backupPath, ideId, file);
|
|
10386
|
-
await
|
|
10387
|
-
await
|
|
10815
|
+
await fs17.mkdir(dirname(destPath), { recursive: true });
|
|
10816
|
+
await fs17.copyFile(originalPath, destPath);
|
|
10388
10817
|
backedUpFiles.push({
|
|
10389
10818
|
originalPath: relativePath,
|
|
10390
10819
|
backupPath: join9(ideId, file),
|
|
@@ -10408,8 +10837,8 @@ var BackupService = class {
|
|
|
10408
10837
|
ides,
|
|
10409
10838
|
files: backedUpFiles
|
|
10410
10839
|
};
|
|
10411
|
-
await
|
|
10412
|
-
await
|
|
10840
|
+
await fs17.mkdir(backupPath, { recursive: true });
|
|
10841
|
+
await fs17.writeFile(
|
|
10413
10842
|
join9(backupPath, "metadata.json"),
|
|
10414
10843
|
JSON.stringify(metadata, null, 2),
|
|
10415
10844
|
"utf-8"
|
|
@@ -10427,8 +10856,8 @@ var BackupService = class {
|
|
|
10427
10856
|
return;
|
|
10428
10857
|
}
|
|
10429
10858
|
const destPath = join9(backupPath, ideId, filename);
|
|
10430
|
-
await
|
|
10431
|
-
await
|
|
10859
|
+
await fs17.mkdir(dirname(destPath), { recursive: true });
|
|
10860
|
+
await fs17.copyFile(originalPath, destPath);
|
|
10432
10861
|
backedUpFiles.push({
|
|
10433
10862
|
originalPath: filename,
|
|
10434
10863
|
backupPath: join9(ideId, filename),
|
|
@@ -10442,15 +10871,15 @@ var BackupService = class {
|
|
|
10442
10871
|
*/
|
|
10443
10872
|
async restoreBackup(backupPath) {
|
|
10444
10873
|
const metadataPath = join9(backupPath, "metadata.json");
|
|
10445
|
-
const metadataContent = await
|
|
10874
|
+
const metadataContent = await fs17.readFile(metadataPath, "utf-8");
|
|
10446
10875
|
const metadata = JSON.parse(metadataContent);
|
|
10447
10876
|
console.log(`
|
|
10448
10877
|
Restoring backup from ${metadata.timestamp}...`);
|
|
10449
10878
|
for (const file of metadata.files) {
|
|
10450
10879
|
const sourcePath = join9(backupPath, file.backupPath);
|
|
10451
10880
|
const destPath = join9(process.cwd(), file.originalPath);
|
|
10452
|
-
await
|
|
10453
|
-
await
|
|
10881
|
+
await fs17.mkdir(dirname(destPath), { recursive: true });
|
|
10882
|
+
await fs17.copyFile(sourcePath, destPath);
|
|
10454
10883
|
console.log(`\u2713 Restored ${file.originalPath}`);
|
|
10455
10884
|
}
|
|
10456
10885
|
console.log("\n\u2705 Backup restored successfully!");
|
|
@@ -10465,13 +10894,13 @@ Restoring backup from ${metadata.timestamp}...`);
|
|
|
10465
10894
|
if (!exists) {
|
|
10466
10895
|
return [];
|
|
10467
10896
|
}
|
|
10468
|
-
const entries = await
|
|
10897
|
+
const entries = await fs17.readdir(backupDirPath, { withFileTypes: true });
|
|
10469
10898
|
const backups = [];
|
|
10470
10899
|
for (const entry of entries) {
|
|
10471
10900
|
if (entry.isDirectory()) {
|
|
10472
10901
|
const metadataPath = join9(backupDirPath, entry.name, "metadata.json");
|
|
10473
10902
|
try {
|
|
10474
|
-
const metadataContent = await
|
|
10903
|
+
const metadataContent = await fs17.readFile(metadataPath, "utf-8");
|
|
10475
10904
|
const metadata = JSON.parse(metadataContent);
|
|
10476
10905
|
backups.push(metadata);
|
|
10477
10906
|
} catch {
|
|
@@ -10518,9 +10947,9 @@ Restoring backup from ${metadata.timestamp}...`);
|
|
|
10518
10947
|
/**
|
|
10519
10948
|
* Check if a path exists
|
|
10520
10949
|
*/
|
|
10521
|
-
async pathExists(
|
|
10950
|
+
async pathExists(path10) {
|
|
10522
10951
|
try {
|
|
10523
|
-
await
|
|
10952
|
+
await fs17.access(path10);
|
|
10524
10953
|
return true;
|
|
10525
10954
|
} catch {
|
|
10526
10955
|
return false;
|
|
@@ -10529,22 +10958,22 @@ Restoring backup from ${metadata.timestamp}...`);
|
|
|
10529
10958
|
/**
|
|
10530
10959
|
* Recursively delete a directory
|
|
10531
10960
|
*/
|
|
10532
|
-
async deleteDirectory(
|
|
10961
|
+
async deleteDirectory(path10) {
|
|
10533
10962
|
try {
|
|
10534
|
-
const exists = await this.pathExists(
|
|
10963
|
+
const exists = await this.pathExists(path10);
|
|
10535
10964
|
if (!exists) {
|
|
10536
10965
|
return;
|
|
10537
10966
|
}
|
|
10538
|
-
const entries = await
|
|
10967
|
+
const entries = await fs17.readdir(path10, { withFileTypes: true });
|
|
10539
10968
|
for (const entry of entries) {
|
|
10540
|
-
const fullPath = join9(
|
|
10969
|
+
const fullPath = join9(path10, entry.name);
|
|
10541
10970
|
if (entry.isDirectory()) {
|
|
10542
10971
|
await this.deleteDirectory(fullPath);
|
|
10543
10972
|
} else {
|
|
10544
|
-
await
|
|
10973
|
+
await fs17.unlink(fullPath);
|
|
10545
10974
|
}
|
|
10546
10975
|
}
|
|
10547
|
-
await
|
|
10976
|
+
await fs17.rmdir(path10);
|
|
10548
10977
|
} catch (error) {
|
|
10549
10978
|
}
|
|
10550
10979
|
}
|
|
@@ -10559,7 +10988,7 @@ Restoring backup from ${metadata.timestamp}...`);
|
|
|
10559
10988
|
*/
|
|
10560
10989
|
async ensureBackupDir() {
|
|
10561
10990
|
const backupDirPath = join9(process.cwd(), this.backupDir);
|
|
10562
|
-
await
|
|
10991
|
+
await fs17.mkdir(backupDirPath, { recursive: true });
|
|
10563
10992
|
}
|
|
10564
10993
|
};
|
|
10565
10994
|
|
|
@@ -10717,19 +11146,19 @@ function createRulesApplyCommand() {
|
|
|
10717
11146
|
console.log("\n\u{1F4DD} Applying preset...\n");
|
|
10718
11147
|
const rulePresetDir = join10(process.cwd(), ".jai1", "rule-preset");
|
|
10719
11148
|
try {
|
|
10720
|
-
await
|
|
11149
|
+
await fs18.rm(rulePresetDir, { recursive: true, force: true });
|
|
10721
11150
|
} catch {
|
|
10722
11151
|
}
|
|
10723
|
-
await
|
|
10724
|
-
await
|
|
11152
|
+
await fs18.mkdir(rulePresetDir, { recursive: true });
|
|
11153
|
+
await fs18.writeFile(
|
|
10725
11154
|
join10(rulePresetDir, "preset.json"),
|
|
10726
11155
|
JSON.stringify(bundle.preset, null, 2),
|
|
10727
11156
|
"utf-8"
|
|
10728
11157
|
);
|
|
10729
11158
|
for (const [filename, content] of Object.entries(bundle.files)) {
|
|
10730
11159
|
const filePath = join10(rulePresetDir, filename);
|
|
10731
|
-
await
|
|
10732
|
-
await
|
|
11160
|
+
await fs18.mkdir(join10(filePath, ".."), { recursive: true });
|
|
11161
|
+
await fs18.writeFile(filePath, content, "utf-8");
|
|
10733
11162
|
}
|
|
10734
11163
|
console.log(`\u2713 Saved preset to .jai1/rule-preset/`);
|
|
10735
11164
|
const allGeneratedFiles = [];
|
|
@@ -10738,8 +11167,8 @@ function createRulesApplyCommand() {
|
|
|
10738
11167
|
const files = generatorService.generateForIde(bundle, ideId);
|
|
10739
11168
|
for (const file of files) {
|
|
10740
11169
|
const fullPath = join10(process.cwd(), file.path);
|
|
10741
|
-
await
|
|
10742
|
-
await
|
|
11170
|
+
await fs18.mkdir(join10(fullPath, ".."), { recursive: true });
|
|
11171
|
+
await fs18.writeFile(fullPath, file.content, "utf-8");
|
|
10743
11172
|
console.log(`\u2713 [${ideId}] ${file.path}`);
|
|
10744
11173
|
allGeneratedFiles.push({
|
|
10745
11174
|
ide: ideId,
|
|
@@ -10767,7 +11196,7 @@ function createRulesApplyCommand() {
|
|
|
10767
11196
|
};
|
|
10768
11197
|
try {
|
|
10769
11198
|
const existingConfigPath = join10(process.cwd(), "jai1-rules.json");
|
|
10770
|
-
const existingConfigContent = await
|
|
11199
|
+
const existingConfigContent = await fs18.readFile(existingConfigPath, "utf-8");
|
|
10771
11200
|
const existingConfig = JSON.parse(existingConfigContent);
|
|
10772
11201
|
if (existingConfig.backups && existingConfig.backups.length > 0) {
|
|
10773
11202
|
projectConfig.backups = [
|
|
@@ -10778,7 +11207,7 @@ function createRulesApplyCommand() {
|
|
|
10778
11207
|
}
|
|
10779
11208
|
} catch {
|
|
10780
11209
|
}
|
|
10781
|
-
await
|
|
11210
|
+
await fs18.writeFile(
|
|
10782
11211
|
join10(process.cwd(), "jai1-rules.json"),
|
|
10783
11212
|
JSON.stringify(projectConfig, null, 2),
|
|
10784
11213
|
"utf-8"
|
|
@@ -10883,7 +11312,7 @@ function formatTimestamp(timestamp) {
|
|
|
10883
11312
|
|
|
10884
11313
|
// src/commands/rules/sync.ts
|
|
10885
11314
|
import { Command as Command46 } from "commander";
|
|
10886
|
-
import { promises as
|
|
11315
|
+
import { promises as fs19 } from "fs";
|
|
10887
11316
|
import { join as join12 } from "path";
|
|
10888
11317
|
import { confirm as confirm10 } from "@inquirer/prompts";
|
|
10889
11318
|
function createRulesSyncCommand() {
|
|
@@ -10891,7 +11320,7 @@ function createRulesSyncCommand() {
|
|
|
10891
11320
|
const configPath = join12(process.cwd(), "jai1-rules.json");
|
|
10892
11321
|
let projectConfig;
|
|
10893
11322
|
try {
|
|
10894
|
-
const configContent = await
|
|
11323
|
+
const configContent = await fs19.readFile(configPath, "utf-8");
|
|
10895
11324
|
projectConfig = JSON.parse(configContent);
|
|
10896
11325
|
} catch {
|
|
10897
11326
|
throw new ValidationError(
|
|
@@ -10974,11 +11403,11 @@ Detected ${detected.length} active IDE(s):
|
|
|
10974
11403
|
const rulePresetDir = join12(process.cwd(), ".jai1", "rule-preset");
|
|
10975
11404
|
const presetExists = await checkPathExists(rulePresetDir);
|
|
10976
11405
|
if (presetExists) {
|
|
10977
|
-
const files = await
|
|
11406
|
+
const files = await fs19.readdir(rulePresetDir);
|
|
10978
11407
|
for (const file of files) {
|
|
10979
11408
|
if (file.endsWith(".mdc")) {
|
|
10980
11409
|
const filePath = join12(rulePresetDir, file);
|
|
10981
|
-
const content = await
|
|
11410
|
+
const content = await fs19.readFile(filePath, "utf-8");
|
|
10982
11411
|
bundle.files[file] = content;
|
|
10983
11412
|
}
|
|
10984
11413
|
}
|
|
@@ -10998,8 +11427,8 @@ Detected ${detected.length} active IDE(s):
|
|
|
10998
11427
|
const files = generatorService.generateForIde(bundle, ideId);
|
|
10999
11428
|
for (const file of files) {
|
|
11000
11429
|
const fullPath = join12(process.cwd(), file.path);
|
|
11001
|
-
await
|
|
11002
|
-
await
|
|
11430
|
+
await fs19.mkdir(join12(fullPath, ".."), { recursive: true });
|
|
11431
|
+
await fs19.writeFile(fullPath, file.content, "utf-8");
|
|
11003
11432
|
}
|
|
11004
11433
|
console.log(`\u2713 ${format.name} - ${files.length} files regenerated`);
|
|
11005
11434
|
} catch (error) {
|
|
@@ -11008,7 +11437,7 @@ Detected ${detected.length} active IDE(s):
|
|
|
11008
11437
|
}
|
|
11009
11438
|
if (JSON.stringify(projectConfig.ides) !== JSON.stringify(idesToSync)) {
|
|
11010
11439
|
projectConfig.ides = idesToSync;
|
|
11011
|
-
await
|
|
11440
|
+
await fs19.writeFile(
|
|
11012
11441
|
join12(process.cwd(), "jai1-rules.json"),
|
|
11013
11442
|
JSON.stringify(projectConfig, null, 2),
|
|
11014
11443
|
"utf-8"
|
|
@@ -11024,7 +11453,7 @@ Detected ${detected.length} active IDE(s):
|
|
|
11024
11453
|
}
|
|
11025
11454
|
async function checkPathExists(absolutePath) {
|
|
11026
11455
|
try {
|
|
11027
|
-
await
|
|
11456
|
+
await fs19.access(absolutePath);
|
|
11028
11457
|
return true;
|
|
11029
11458
|
} catch {
|
|
11030
11459
|
return false;
|
|
@@ -11033,14 +11462,14 @@ async function checkPathExists(absolutePath) {
|
|
|
11033
11462
|
|
|
11034
11463
|
// src/commands/rules/info.ts
|
|
11035
11464
|
import { Command as Command47 } from "commander";
|
|
11036
|
-
import { promises as
|
|
11465
|
+
import { promises as fs20 } from "fs";
|
|
11037
11466
|
import { join as join13 } from "path";
|
|
11038
11467
|
function createRulesInfoCommand() {
|
|
11039
11468
|
return new Command47("info").description("Show current preset information").option("--json", "Output as JSON").action(async (options) => {
|
|
11040
11469
|
const configPath = join13(process.cwd(), "jai1-rules.json");
|
|
11041
11470
|
let projectConfig;
|
|
11042
11471
|
try {
|
|
11043
|
-
const configContent = await
|
|
11472
|
+
const configContent = await fs20.readFile(configPath, "utf-8");
|
|
11044
11473
|
projectConfig = JSON.parse(configContent);
|
|
11045
11474
|
} catch {
|
|
11046
11475
|
throw new ValidationError(
|
|
@@ -11057,9 +11486,9 @@ function createRulesInfoCommand() {
|
|
|
11057
11486
|
let presetMetadata = null;
|
|
11058
11487
|
let presetFiles = [];
|
|
11059
11488
|
try {
|
|
11060
|
-
const presetContent = await
|
|
11489
|
+
const presetContent = await fs20.readFile(presetJsonPath, "utf-8");
|
|
11061
11490
|
presetMetadata = JSON.parse(presetContent);
|
|
11062
|
-
const files = await
|
|
11491
|
+
const files = await fs20.readdir(rulePresetDir);
|
|
11063
11492
|
presetFiles = files.filter((f) => f.endsWith(".mdc"));
|
|
11064
11493
|
} catch {
|
|
11065
11494
|
}
|
|
@@ -11118,9 +11547,9 @@ Available Backups (${projectConfig.backups.length}):`);
|
|
|
11118
11547
|
console.log(' \u2022 "jai1 rules apply" - Apply a different preset (replaces current)');
|
|
11119
11548
|
});
|
|
11120
11549
|
}
|
|
11121
|
-
async function checkPathExists2(
|
|
11550
|
+
async function checkPathExists2(path10) {
|
|
11122
11551
|
try {
|
|
11123
|
-
await
|
|
11552
|
+
await fs20.access(join13(process.cwd(), path10));
|
|
11124
11553
|
return true;
|
|
11125
11554
|
} catch {
|
|
11126
11555
|
return false;
|
|
@@ -11551,8 +11980,8 @@ var RedmineApiClient = class {
|
|
|
11551
11980
|
this.retryConfig = config.defaults.retry;
|
|
11552
11981
|
this.concurrencyLimit = pLimit3(config.defaults.concurrency);
|
|
11553
11982
|
}
|
|
11554
|
-
async request(
|
|
11555
|
-
const url = `${this.baseUrl}${
|
|
11983
|
+
async request(path10, options = {}) {
|
|
11984
|
+
const url = `${this.baseUrl}${path10}`;
|
|
11556
11985
|
const headers = {
|
|
11557
11986
|
"X-Redmine-API-Key": this.apiAccessToken,
|
|
11558
11987
|
"Content-Type": "application/json",
|
|
@@ -11613,8 +12042,8 @@ var RedmineApiClient = class {
|
|
|
11613
12042
|
if (include && include.length > 0) {
|
|
11614
12043
|
params.append("include", include.join(","));
|
|
11615
12044
|
}
|
|
11616
|
-
const
|
|
11617
|
-
return this.request(
|
|
12045
|
+
const path10 = `/issues/${issueId}.json${params.toString() ? `?${params.toString()}` : ""}`;
|
|
12046
|
+
return this.request(path10);
|
|
11618
12047
|
}
|
|
11619
12048
|
async getIssues(projectId, options = {}) {
|
|
11620
12049
|
const params = new URLSearchParams();
|
|
@@ -11634,8 +12063,8 @@ var RedmineApiClient = class {
|
|
|
11634
12063
|
if (options.updatedSince) {
|
|
11635
12064
|
params.append("updated_on", `>=${options.updatedSince}`);
|
|
11636
12065
|
}
|
|
11637
|
-
const
|
|
11638
|
-
return this.request(
|
|
12066
|
+
const path10 = `/issues.json?${params.toString()}`;
|
|
12067
|
+
return this.request(path10);
|
|
11639
12068
|
}
|
|
11640
12069
|
async getAllIssues(projectId, options = {}) {
|
|
11641
12070
|
const pageSize = options.pageSize || 100;
|
|
@@ -12319,7 +12748,7 @@ async function handleSyncProject(options) {
|
|
|
12319
12748
|
|
|
12320
12749
|
// src/commands/framework/info.ts
|
|
12321
12750
|
import { Command as Command54 } from "commander";
|
|
12322
|
-
import { promises as
|
|
12751
|
+
import { promises as fs21 } from "fs";
|
|
12323
12752
|
import { join as join15 } from "path";
|
|
12324
12753
|
import { homedir as homedir5 } from "os";
|
|
12325
12754
|
function createInfoCommand() {
|
|
@@ -12371,7 +12800,7 @@ function maskKey3(key) {
|
|
|
12371
12800
|
async function getProjectStatus2() {
|
|
12372
12801
|
const projectJai1 = join15(process.cwd(), ".jai1");
|
|
12373
12802
|
try {
|
|
12374
|
-
await
|
|
12803
|
+
await fs21.access(projectJai1);
|
|
12375
12804
|
return { exists: true, version: "Synced" };
|
|
12376
12805
|
} catch {
|
|
12377
12806
|
return { exists: false };
|
|
@@ -12561,8 +12990,8 @@ function createClearBackupsCommand() {
|
|
|
12561
12990
|
// src/commands/vscode/index.ts
|
|
12562
12991
|
import { Command as Command57 } from "commander";
|
|
12563
12992
|
import { checkbox as checkbox7, confirm as confirm15, select as select8 } from "@inquirer/prompts";
|
|
12564
|
-
import
|
|
12565
|
-
import
|
|
12993
|
+
import fs22 from "fs/promises";
|
|
12994
|
+
import path9 from "path";
|
|
12566
12995
|
import { existsSync as existsSync3 } from "fs";
|
|
12567
12996
|
var PERFORMANCE_GROUPS2 = {
|
|
12568
12997
|
telemetry: {
|
|
@@ -12782,8 +13211,8 @@ async function selectGroupsToApply2(action) {
|
|
|
12782
13211
|
}
|
|
12783
13212
|
}
|
|
12784
13213
|
async function applyGroups2(groupKeys, action) {
|
|
12785
|
-
const vscodeDir =
|
|
12786
|
-
const settingsPath =
|
|
13214
|
+
const vscodeDir = path9.join(process.cwd(), ".vscode");
|
|
13215
|
+
const settingsPath = path9.join(vscodeDir, "settings.json");
|
|
12787
13216
|
const invalidGroups = groupKeys.filter((key) => !PERFORMANCE_GROUPS2[key]);
|
|
12788
13217
|
if (invalidGroups.length > 0) {
|
|
12789
13218
|
console.log(`
|
|
@@ -12792,13 +13221,13 @@ async function applyGroups2(groupKeys, action) {
|
|
|
12792
13221
|
return;
|
|
12793
13222
|
}
|
|
12794
13223
|
if (!existsSync3(vscodeDir)) {
|
|
12795
|
-
await
|
|
13224
|
+
await fs22.mkdir(vscodeDir, { recursive: true });
|
|
12796
13225
|
console.log("\u{1F4C1} \u0110\xE3 t\u1EA1o th\u01B0 m\u1EE5c .vscode/");
|
|
12797
13226
|
}
|
|
12798
13227
|
let currentSettings = {};
|
|
12799
13228
|
if (existsSync3(settingsPath)) {
|
|
12800
13229
|
try {
|
|
12801
|
-
const content = await
|
|
13230
|
+
const content = await fs22.readFile(settingsPath, "utf-8");
|
|
12802
13231
|
currentSettings = JSON.parse(content);
|
|
12803
13232
|
console.log("\u{1F4C4} \u0110\xE3 \u0111\u1ECDc c\xE0i \u0111\u1EB7t hi\u1EC7n t\u1EA1i t\u1EEB settings.json");
|
|
12804
13233
|
} catch {
|
|
@@ -12838,14 +13267,14 @@ async function applyGroups2(groupKeys, action) {
|
|
|
12838
13267
|
}
|
|
12839
13268
|
}
|
|
12840
13269
|
}
|
|
12841
|
-
await
|
|
13270
|
+
await fs22.writeFile(settingsPath, JSON.stringify(newSettings, null, 2));
|
|
12842
13271
|
console.log(`
|
|
12843
13272
|
\u2705 \u0110\xE3 c\u1EADp nh\u1EADt c\xE0i \u0111\u1EB7t VSCode t\u1EA1i: ${settingsPath}`);
|
|
12844
13273
|
console.log("\u{1F4A1} M\u1EB9o: Kh\u1EDFi \u0111\u1ED9ng l\u1EA1i VSCode \u0111\u1EC3 \xE1p d\u1EE5ng c\xE1c thay \u0111\u1ED5i.");
|
|
12845
13274
|
}
|
|
12846
13275
|
async function resetSettings2(groupKeys) {
|
|
12847
|
-
const vscodeDir =
|
|
12848
|
-
const settingsPath =
|
|
13276
|
+
const vscodeDir = path9.join(process.cwd(), ".vscode");
|
|
13277
|
+
const settingsPath = path9.join(vscodeDir, "settings.json");
|
|
12849
13278
|
if (!existsSync3(settingsPath)) {
|
|
12850
13279
|
console.log("\n\u26A0\uFE0F Kh\xF4ng t\xECm th\u1EA5y file settings.json");
|
|
12851
13280
|
return;
|
|
@@ -12859,7 +13288,7 @@ async function resetSettings2(groupKeys) {
|
|
|
12859
13288
|
return;
|
|
12860
13289
|
}
|
|
12861
13290
|
if (groupKeys.length === 0) {
|
|
12862
|
-
await
|
|
13291
|
+
await fs22.unlink(settingsPath);
|
|
12863
13292
|
console.log("\n\u2705 \u0110\xE3 x\xF3a file settings.json");
|
|
12864
13293
|
} else {
|
|
12865
13294
|
await applyGroups2(groupKeys, "disable");
|