@hasna/terminal 0.7.5 → 0.7.6
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 +29 -0
- package/dist/mcp/server.js +48 -0
- package/package.json +1 -1
- package/src/cli.tsx +23 -0
- package/src/mcp/server.ts +63 -0
package/dist/cli.js
CHANGED
|
@@ -458,6 +458,35 @@ else if (args[0] === "sessions") {
|
|
|
458
458
|
}
|
|
459
459
|
}
|
|
460
460
|
}
|
|
461
|
+
// ── Overview command ─────────────────────────────────────────────────────────
|
|
462
|
+
else if (args[0] === "overview") {
|
|
463
|
+
const { existsSync, readFileSync } = await import("fs");
|
|
464
|
+
const { execSync } = await import("child_process");
|
|
465
|
+
const run = (cmd) => { try {
|
|
466
|
+
return execSync(cmd, { encoding: "utf8", cwd: process.cwd() }).trim();
|
|
467
|
+
}
|
|
468
|
+
catch {
|
|
469
|
+
return "";
|
|
470
|
+
} };
|
|
471
|
+
let pkg = null;
|
|
472
|
+
try {
|
|
473
|
+
pkg = JSON.parse(readFileSync("package.json", "utf8"));
|
|
474
|
+
}
|
|
475
|
+
catch { }
|
|
476
|
+
if (pkg) {
|
|
477
|
+
console.log(`${pkg.name}@${pkg.version}`);
|
|
478
|
+
if (pkg.scripts) {
|
|
479
|
+
console.log("\nScripts:");
|
|
480
|
+
for (const [k, v] of Object.entries(pkg.scripts).slice(0, 10))
|
|
481
|
+
console.log(` ${k}: ${v}`);
|
|
482
|
+
}
|
|
483
|
+
if (pkg.dependencies)
|
|
484
|
+
console.log(`\nDeps: ${Object.keys(pkg.dependencies).join(", ")}`);
|
|
485
|
+
}
|
|
486
|
+
const src = run("ls -1 src/ 2>/dev/null || ls -1 lib/ 2>/dev/null");
|
|
487
|
+
if (src)
|
|
488
|
+
console.log(`\nSource:\n${src.split("\n").map(f => " " + f).join("\n")}`);
|
|
489
|
+
}
|
|
461
490
|
// ── Repo command ─────────────────────────────────────────────────────────────
|
|
462
491
|
else if (args[0] === "repo") {
|
|
463
492
|
const { execSync } = await import("child_process");
|
package/dist/mcp/server.js
CHANGED
|
@@ -416,6 +416,54 @@ export function createServer() {
|
|
|
416
416
|
const sessions = listSessions(limit ?? 20);
|
|
417
417
|
return { content: [{ type: "text", text: JSON.stringify(sessions) }] };
|
|
418
418
|
});
|
|
419
|
+
// ── project_overview: orient agent in one call ─────────────────────────────
|
|
420
|
+
server.tool("project_overview", "Get project overview in one call — package.json info, source structure, config files. Replaces: cat package.json + ls src/ + cat tsconfig.json.", {
|
|
421
|
+
path: z.string().optional().describe("Project root (default: cwd)"),
|
|
422
|
+
}, async ({ path }) => {
|
|
423
|
+
const cwd = path ?? process.cwd();
|
|
424
|
+
const [pkgResult, srcResult, configResult] = await Promise.all([
|
|
425
|
+
exec("cat package.json 2>/dev/null", cwd),
|
|
426
|
+
exec("ls -1 src/ 2>/dev/null || ls -1 lib/ 2>/dev/null || ls -1 app/ 2>/dev/null", cwd),
|
|
427
|
+
exec("ls -1 *.json *.config.* .env* tsconfig* 2>/dev/null", cwd),
|
|
428
|
+
]);
|
|
429
|
+
let pkg = null;
|
|
430
|
+
try {
|
|
431
|
+
pkg = JSON.parse(pkgResult.stdout);
|
|
432
|
+
}
|
|
433
|
+
catch { }
|
|
434
|
+
return {
|
|
435
|
+
content: [{ type: "text", text: JSON.stringify({
|
|
436
|
+
name: pkg?.name,
|
|
437
|
+
version: pkg?.version,
|
|
438
|
+
scripts: pkg?.scripts,
|
|
439
|
+
dependencies: pkg?.dependencies ? Object.keys(pkg.dependencies) : [],
|
|
440
|
+
devDependencies: pkg?.devDependencies ? Object.keys(pkg.devDependencies) : [],
|
|
441
|
+
sourceFiles: srcResult.stdout.split("\n").filter(l => l.trim()),
|
|
442
|
+
configFiles: configResult.stdout.split("\n").filter(l => l.trim()),
|
|
443
|
+
}) }],
|
|
444
|
+
};
|
|
445
|
+
});
|
|
446
|
+
// ── last_commit: what just happened ───────────────────────────────────────
|
|
447
|
+
server.tool("last_commit", "Get details of the last commit — hash, message, files changed, diff stats. Replaces: git log -1 + git show --stat + git diff HEAD~1.", {
|
|
448
|
+
path: z.string().optional().describe("Repo path (default: cwd)"),
|
|
449
|
+
}, async ({ path }) => {
|
|
450
|
+
const cwd = path ?? process.cwd();
|
|
451
|
+
const [logResult, statResult] = await Promise.all([
|
|
452
|
+
exec("git log -1 --format='%H%n%s%n%an%n%ai'", cwd),
|
|
453
|
+
exec("git show --stat --format='' HEAD", cwd),
|
|
454
|
+
]);
|
|
455
|
+
const [hash, message, author, date] = logResult.stdout.split("\n");
|
|
456
|
+
const filesChanged = statResult.stdout.split("\n").filter(l => l.trim() && !l.includes("changed"));
|
|
457
|
+
return {
|
|
458
|
+
content: [{ type: "text", text: JSON.stringify({
|
|
459
|
+
hash: hash?.trim(),
|
|
460
|
+
message: message?.trim(),
|
|
461
|
+
author: author?.trim(),
|
|
462
|
+
date: date?.trim(),
|
|
463
|
+
filesChanged,
|
|
464
|
+
}) }],
|
|
465
|
+
};
|
|
466
|
+
});
|
|
419
467
|
// ── read_file: cached file reading ─────────────────────────────────────────
|
|
420
468
|
server.tool("read_file", "Read a file with session caching. Second read of unchanged file returns instantly from cache. Supports offset/limit for pagination without re-reading.", {
|
|
421
469
|
path: z.string().describe("File path"),
|
package/package.json
CHANGED
package/src/cli.tsx
CHANGED
|
@@ -443,6 +443,29 @@ else if (args[0] === "sessions") {
|
|
|
443
443
|
}
|
|
444
444
|
}
|
|
445
445
|
|
|
446
|
+
// ── Overview command ─────────────────────────────────────────────────────────
|
|
447
|
+
|
|
448
|
+
else if (args[0] === "overview") {
|
|
449
|
+
const { existsSync, readFileSync } = await import("fs");
|
|
450
|
+
const { execSync } = await import("child_process");
|
|
451
|
+
const run = (cmd: string) => { try { return execSync(cmd, { encoding: "utf8", cwd: process.cwd() }).trim(); } catch { return ""; } };
|
|
452
|
+
|
|
453
|
+
let pkg: any = null;
|
|
454
|
+
try { pkg = JSON.parse(readFileSync("package.json", "utf8")); } catch {}
|
|
455
|
+
|
|
456
|
+
if (pkg) {
|
|
457
|
+
console.log(`${pkg.name}@${pkg.version}`);
|
|
458
|
+
if (pkg.scripts) {
|
|
459
|
+
console.log("\nScripts:");
|
|
460
|
+
for (const [k, v] of Object.entries(pkg.scripts).slice(0, 10)) console.log(` ${k}: ${v}`);
|
|
461
|
+
}
|
|
462
|
+
if (pkg.dependencies) console.log(`\nDeps: ${Object.keys(pkg.dependencies).join(", ")}`);
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
const src = run("ls -1 src/ 2>/dev/null || ls -1 lib/ 2>/dev/null");
|
|
466
|
+
if (src) console.log(`\nSource:\n${src.split("\n").map(f => " " + f).join("\n")}`);
|
|
467
|
+
}
|
|
468
|
+
|
|
446
469
|
// ── Repo command ─────────────────────────────────────────────────────────────
|
|
447
470
|
|
|
448
471
|
else if (args[0] === "repo") {
|
package/src/mcp/server.ts
CHANGED
|
@@ -587,6 +587,69 @@ export function createServer(): McpServer {
|
|
|
587
587
|
}
|
|
588
588
|
);
|
|
589
589
|
|
|
590
|
+
// ── project_overview: orient agent in one call ─────────────────────────────
|
|
591
|
+
|
|
592
|
+
server.tool(
|
|
593
|
+
"project_overview",
|
|
594
|
+
"Get project overview in one call — package.json info, source structure, config files. Replaces: cat package.json + ls src/ + cat tsconfig.json.",
|
|
595
|
+
{
|
|
596
|
+
path: z.string().optional().describe("Project root (default: cwd)"),
|
|
597
|
+
},
|
|
598
|
+
async ({ path }) => {
|
|
599
|
+
const cwd = path ?? process.cwd();
|
|
600
|
+
const [pkgResult, srcResult, configResult] = await Promise.all([
|
|
601
|
+
exec("cat package.json 2>/dev/null", cwd),
|
|
602
|
+
exec("ls -1 src/ 2>/dev/null || ls -1 lib/ 2>/dev/null || ls -1 app/ 2>/dev/null", cwd),
|
|
603
|
+
exec("ls -1 *.json *.config.* .env* tsconfig* 2>/dev/null", cwd),
|
|
604
|
+
]);
|
|
605
|
+
|
|
606
|
+
let pkg: any = null;
|
|
607
|
+
try { pkg = JSON.parse(pkgResult.stdout); } catch {}
|
|
608
|
+
|
|
609
|
+
return {
|
|
610
|
+
content: [{ type: "text" as const, text: JSON.stringify({
|
|
611
|
+
name: pkg?.name,
|
|
612
|
+
version: pkg?.version,
|
|
613
|
+
scripts: pkg?.scripts,
|
|
614
|
+
dependencies: pkg?.dependencies ? Object.keys(pkg.dependencies) : [],
|
|
615
|
+
devDependencies: pkg?.devDependencies ? Object.keys(pkg.devDependencies) : [],
|
|
616
|
+
sourceFiles: srcResult.stdout.split("\n").filter(l => l.trim()),
|
|
617
|
+
configFiles: configResult.stdout.split("\n").filter(l => l.trim()),
|
|
618
|
+
}) }],
|
|
619
|
+
};
|
|
620
|
+
}
|
|
621
|
+
);
|
|
622
|
+
|
|
623
|
+
// ── last_commit: what just happened ───────────────────────────────────────
|
|
624
|
+
|
|
625
|
+
server.tool(
|
|
626
|
+
"last_commit",
|
|
627
|
+
"Get details of the last commit — hash, message, files changed, diff stats. Replaces: git log -1 + git show --stat + git diff HEAD~1.",
|
|
628
|
+
{
|
|
629
|
+
path: z.string().optional().describe("Repo path (default: cwd)"),
|
|
630
|
+
},
|
|
631
|
+
async ({ path }) => {
|
|
632
|
+
const cwd = path ?? process.cwd();
|
|
633
|
+
const [logResult, statResult] = await Promise.all([
|
|
634
|
+
exec("git log -1 --format='%H%n%s%n%an%n%ai'", cwd),
|
|
635
|
+
exec("git show --stat --format='' HEAD", cwd),
|
|
636
|
+
]);
|
|
637
|
+
|
|
638
|
+
const [hash, message, author, date] = logResult.stdout.split("\n");
|
|
639
|
+
const filesChanged = statResult.stdout.split("\n").filter(l => l.trim() && !l.includes("changed"));
|
|
640
|
+
|
|
641
|
+
return {
|
|
642
|
+
content: [{ type: "text" as const, text: JSON.stringify({
|
|
643
|
+
hash: hash?.trim(),
|
|
644
|
+
message: message?.trim(),
|
|
645
|
+
author: author?.trim(),
|
|
646
|
+
date: date?.trim(),
|
|
647
|
+
filesChanged,
|
|
648
|
+
}) }],
|
|
649
|
+
};
|
|
650
|
+
}
|
|
651
|
+
);
|
|
652
|
+
|
|
590
653
|
// ── read_file: cached file reading ─────────────────────────────────────────
|
|
591
654
|
|
|
592
655
|
server.tool(
|