antri_cli 1.57.18 ā 1.57.20
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/core/agent.d.ts.map +1 -1
- package/dist/core/agent.js +3 -5
- package/dist/core/agent.js.map +1 -1
- package/dist/core/tools.d.ts +1 -0
- package/dist/core/tools.d.ts.map +1 -1
- package/dist/core/tools.js +650 -85
- package/dist/core/tools.js.map +1 -1
- package/dist/core/updater.d.ts +1 -1
- package/dist/core/updater.js +1 -1
- package/dist/desktop/public/index.html +1 -1
- package/dist/skills/skillManager.d.ts +6 -0
- package/dist/skills/skillManager.d.ts.map +1 -1
- package/dist/skills/skillManager.js +26 -0
- package/dist/skills/skillManager.js.map +1 -1
- package/package.json +1 -1
package/dist/core/tools.js
CHANGED
|
@@ -484,6 +484,379 @@ export class ToolExecutor {
|
|
|
484
484
|
static isSensitive(name) {
|
|
485
485
|
return SENSITIVE_TOOLS.has(name);
|
|
486
486
|
}
|
|
487
|
+
static materializeNextJsPortfolio(workingDir) {
|
|
488
|
+
const files = {
|
|
489
|
+
'package.json': JSON.stringify({
|
|
490
|
+
name: 'portfolio-website',
|
|
491
|
+
version: '0.1.0',
|
|
492
|
+
private: true,
|
|
493
|
+
scripts: {
|
|
494
|
+
dev: 'next dev',
|
|
495
|
+
build: 'next build',
|
|
496
|
+
start: 'next start',
|
|
497
|
+
},
|
|
498
|
+
dependencies: {
|
|
499
|
+
next: '^14.2.5',
|
|
500
|
+
react: '^18.3.1',
|
|
501
|
+
'react-dom': '^18.3.1',
|
|
502
|
+
'lucide-react': '^0.428.0',
|
|
503
|
+
clsx: '^2.1.1',
|
|
504
|
+
'tailwind-merge': '^2.5.2',
|
|
505
|
+
},
|
|
506
|
+
devDependencies: {
|
|
507
|
+
typescript: '^5.5.4',
|
|
508
|
+
'@types/node': '^20.14.14',
|
|
509
|
+
'@types/react': '^18.3.3',
|
|
510
|
+
'@types/react-dom': '^18.3.0',
|
|
511
|
+
postcss: '^8.4.41',
|
|
512
|
+
tailwindcss: '^3.4.10',
|
|
513
|
+
autoprefixer: '^10.4.20',
|
|
514
|
+
},
|
|
515
|
+
}, null, 2),
|
|
516
|
+
'tsconfig.json': JSON.stringify({
|
|
517
|
+
compilerOptions: {
|
|
518
|
+
target: 'es5',
|
|
519
|
+
lib: ['dom', 'dom.iterable', 'esnext'],
|
|
520
|
+
allowJs: true,
|
|
521
|
+
skipLibCheck: true,
|
|
522
|
+
strict: true,
|
|
523
|
+
noEmit: true,
|
|
524
|
+
esModuleInterop: true,
|
|
525
|
+
module: 'esnext',
|
|
526
|
+
moduleResolution: 'bundler',
|
|
527
|
+
resolveJsonModule: true,
|
|
528
|
+
isolatedModules: true,
|
|
529
|
+
jsx: 'preserve',
|
|
530
|
+
incremental: true,
|
|
531
|
+
plugins: [{ name: 'next' }],
|
|
532
|
+
paths: { '@/*': ['./*'] },
|
|
533
|
+
},
|
|
534
|
+
include: ['next-env.d.ts', '**/*.ts', '**/*.tsx', '.next/types/**/*.ts'],
|
|
535
|
+
exclude: ['node_modules'],
|
|
536
|
+
}, null, 2),
|
|
537
|
+
'tailwind.config.js': `/** @type {import('tailwindcss').Config} */
|
|
538
|
+
module.exports = {
|
|
539
|
+
content: [
|
|
540
|
+
'./pages/**/*.{js,ts,jsx,tsx,mdx}',
|
|
541
|
+
'./components/**/*.{js,ts,jsx,tsx,mdx}',
|
|
542
|
+
'./app/**/*.{js,ts,jsx,tsx,mdx}',
|
|
543
|
+
],
|
|
544
|
+
theme: {
|
|
545
|
+
extend: {},
|
|
546
|
+
},
|
|
547
|
+
plugins: [],
|
|
548
|
+
};`,
|
|
549
|
+
'postcss.config.js': `module.exports = {
|
|
550
|
+
plugins: {
|
|
551
|
+
tailwindcss: {},
|
|
552
|
+
autoprefixer: {},
|
|
553
|
+
},
|
|
554
|
+
};`,
|
|
555
|
+
'app/layout.tsx': `import type { Metadata } from 'next';
|
|
556
|
+
import './globals.css';
|
|
557
|
+
|
|
558
|
+
export const metadata: Metadata = {
|
|
559
|
+
title: 'Full-Stack Developer Portfolio',
|
|
560
|
+
description: 'Modern Portfolio built with Next.js, React, & Tailwind CSS',
|
|
561
|
+
};
|
|
562
|
+
|
|
563
|
+
export default function RootLayout({
|
|
564
|
+
children,
|
|
565
|
+
}: {
|
|
566
|
+
children: React.ReactNode;
|
|
567
|
+
}) {
|
|
568
|
+
return (
|
|
569
|
+
<html lang="en" className="scroll-smooth">
|
|
570
|
+
<body className="bg-slate-950 text-slate-100 min-h-screen antialiased selection:bg-indigo-500 selection:text-white">
|
|
571
|
+
{children}
|
|
572
|
+
</body>
|
|
573
|
+
</html>
|
|
574
|
+
);
|
|
575
|
+
}`,
|
|
576
|
+
'app/globals.css': `@tailwind base;
|
|
577
|
+
@tailwind components;
|
|
578
|
+
@tailwind utilities;
|
|
579
|
+
|
|
580
|
+
@layer base {
|
|
581
|
+
body {
|
|
582
|
+
@apply bg-slate-950 text-slate-100;
|
|
583
|
+
}
|
|
584
|
+
}`,
|
|
585
|
+
'components/Navbar.tsx': `'use client';
|
|
586
|
+
import React, { useState } from 'react';
|
|
587
|
+
import { Code2, Menu, X } from 'lucide-react';
|
|
588
|
+
|
|
589
|
+
export default function Navbar() {
|
|
590
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
591
|
+
|
|
592
|
+
return (
|
|
593
|
+
<nav className="fixed top-0 left-0 right-0 z-50 bg-slate-950/80 backdrop-blur-md border-b border-slate-800/80">
|
|
594
|
+
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
|
595
|
+
<div className="flex items-center justify-between h-16">
|
|
596
|
+
<div className="flex items-center space-x-3">
|
|
597
|
+
<div className="w-10 h-10 rounded-xl bg-gradient-to-tr from-indigo-600 to-cyan-500 flex items-center justify-center shadow-lg shadow-indigo-500/25">
|
|
598
|
+
<Code2 className="w-6 h-6 text-white" />
|
|
599
|
+
</div>
|
|
600
|
+
<span className="font-bold text-xl bg-clip-text text-transparent bg-gradient-to-r from-white via-slate-200 to-indigo-400">
|
|
601
|
+
DevPortfolio
|
|
602
|
+
</span>
|
|
603
|
+
</div>
|
|
604
|
+
|
|
605
|
+
<div className="hidden md:flex items-center space-x-8">
|
|
606
|
+
<a href="#home" className="text-sm font-medium text-slate-300 hover:text-white transition-colors">Home</a>
|
|
607
|
+
<a href="#about" className="text-sm font-medium text-slate-300 hover:text-white transition-colors">About</a>
|
|
608
|
+
<a href="#projects" className="text-sm font-medium text-slate-300 hover:text-white transition-colors">Projects</a>
|
|
609
|
+
<a href="#skills" className="text-sm font-medium text-slate-300 hover:text-white transition-colors">Skills</a>
|
|
610
|
+
<a href="#contact" className="px-4 py-2 rounded-lg bg-indigo-600 hover:bg-indigo-500 text-white text-sm font-medium shadow-md shadow-indigo-600/30 transition-all">
|
|
611
|
+
Contact Me
|
|
612
|
+
</a>
|
|
613
|
+
</div>
|
|
614
|
+
|
|
615
|
+
<div className="md:hidden flex items-center">
|
|
616
|
+
<button
|
|
617
|
+
onClick={() => setIsOpen(!isOpen)}
|
|
618
|
+
className="text-slate-400 hover:text-white focus:outline-none"
|
|
619
|
+
>
|
|
620
|
+
{isOpen ? <X className="w-6 h-6" /> : <Menu className="w-6 h-6" />}
|
|
621
|
+
</button>
|
|
622
|
+
</div>
|
|
623
|
+
</div>
|
|
624
|
+
</div>
|
|
625
|
+
|
|
626
|
+
{isOpen && (
|
|
627
|
+
<div className="md:hidden bg-slate-900 border-b border-slate-800 px-4 pt-2 pb-4 space-y-2">
|
|
628
|
+
<a href="#home" onClick={() => setIsOpen(false)} className="block py-2 text-slate-300 hover:text-white">Home</a>
|
|
629
|
+
<a href="#about" onClick={() => setIsOpen(false)} className="block py-2 text-slate-300 hover:text-white">About</a>
|
|
630
|
+
<a href="#projects" onClick={() => setIsOpen(false)} className="block py-2 text-slate-300 hover:text-white">Projects</a>
|
|
631
|
+
<a href="#skills" onClick={() => setIsOpen(false)} className="block py-2 text-slate-300 hover:text-white">Skills</a>
|
|
632
|
+
<a href="#contact" onClick={() => setIsOpen(false)} className="block py-2 text-indigo-400 font-semibold">Contact</a>
|
|
633
|
+
</div>
|
|
634
|
+
)}
|
|
635
|
+
</nav>
|
|
636
|
+
);
|
|
637
|
+
}`,
|
|
638
|
+
'components/Hero.tsx': `import React from 'react';
|
|
639
|
+
import { ArrowRight, Sparkles } from 'lucide-react';
|
|
640
|
+
|
|
641
|
+
export default function Hero() {
|
|
642
|
+
return (
|
|
643
|
+
<section id="home" className="pt-32 pb-20 px-4 sm:px-6 lg:px-8 max-w-7xl mx-auto flex flex-col items-center text-center">
|
|
644
|
+
<div className="inline-flex items-center space-x-2 px-3 py-1.5 rounded-full border border-indigo-500/30 bg-indigo-500/10 text-indigo-400 text-xs font-medium mb-6">
|
|
645
|
+
<Sparkles className="w-4 h-4" />
|
|
646
|
+
<span>Available for Full-Stack & Systems Engineering</span>
|
|
647
|
+
</div>
|
|
648
|
+
|
|
649
|
+
<h1 className="text-4xl sm:text-6xl lg:text-7xl font-extrabold tracking-tight text-white max-w-4xl">
|
|
650
|
+
Building modern, scalable software with <span className="bg-clip-text text-transparent bg-gradient-to-r from-indigo-400 via-purple-400 to-cyan-400">precision & speed</span>.
|
|
651
|
+
</h1>
|
|
652
|
+
|
|
653
|
+
<p className="mt-6 text-lg sm:text-xl text-slate-400 max-w-2xl">
|
|
654
|
+
Full-Stack Software Engineer specializing in React, Next.js, TypeScript, Node.js, and Distributed Cloud Architecture.
|
|
655
|
+
</p>
|
|
656
|
+
|
|
657
|
+
<div className="mt-8 flex flex-wrap gap-4 justify-center">
|
|
658
|
+
<a
|
|
659
|
+
href="#projects"
|
|
660
|
+
className="inline-flex items-center space-x-2 px-6 py-3 rounded-xl bg-gradient-to-r from-indigo-600 to-cyan-600 hover:from-indigo-500 hover:to-cyan-500 text-white font-semibold shadow-lg shadow-indigo-500/25 transition-all"
|
|
661
|
+
>
|
|
662
|
+
<span>View Projects</span>
|
|
663
|
+
<ArrowRight className="w-4 h-4" />
|
|
664
|
+
</a>
|
|
665
|
+
<a
|
|
666
|
+
href="#contact"
|
|
667
|
+
className="inline-flex items-center space-x-2 px-6 py-3 rounded-xl border border-slate-700 hover:border-slate-600 bg-slate-900/50 hover:bg-slate-800 text-slate-200 font-semibold transition-all"
|
|
668
|
+
>
|
|
669
|
+
<span>Get in Touch</span>
|
|
670
|
+
</a>
|
|
671
|
+
</div>
|
|
672
|
+
</section>
|
|
673
|
+
);
|
|
674
|
+
}`,
|
|
675
|
+
'components/Projects.tsx': `import React from 'react';
|
|
676
|
+
import { ExternalLink, Github, Layers } from 'lucide-react';
|
|
677
|
+
|
|
678
|
+
const projects = [
|
|
679
|
+
{
|
|
680
|
+
title: 'Autonomous Meta-Agent CLI',
|
|
681
|
+
description: 'Terminal-first cognitive assistant with multi-tier memory, real-time tool execution, and self-healing diagnostics.',
|
|
682
|
+
tags: ['TypeScript', 'Node.js', 'AI Agent', 'ESM'],
|
|
683
|
+
github: 'https://github.com',
|
|
684
|
+
link: '#',
|
|
685
|
+
},
|
|
686
|
+
{
|
|
687
|
+
title: 'Distributed Cloud Microservices',
|
|
688
|
+
description: 'High-throughput event-driven microservices architecture with real-time stream synchronization and Redis caching.',
|
|
689
|
+
tags: ['Next.js', 'React', 'Tailwind CSS', 'PostgreSQL'],
|
|
690
|
+
github: 'https://github.com',
|
|
691
|
+
link: '#',
|
|
692
|
+
},
|
|
693
|
+
{
|
|
694
|
+
title: 'Interactive Real-Time Analytics Dashboard',
|
|
695
|
+
description: 'Interactive analytics visualization platform with responsive glassmorphism UI and sub-millisecond metrics telemetry.',
|
|
696
|
+
tags: ['React', 'TypeScript', 'Tailwind', 'Chart.js'],
|
|
697
|
+
github: 'https://github.com',
|
|
698
|
+
link: '#',
|
|
699
|
+
},
|
|
700
|
+
];
|
|
701
|
+
|
|
702
|
+
export default function Projects() {
|
|
703
|
+
return (
|
|
704
|
+
<section id="projects" className="py-20 px-4 sm:px-6 lg:px-8 max-w-7xl mx-auto">
|
|
705
|
+
<div className="text-center mb-12">
|
|
706
|
+
<h2 className="text-3xl sm:text-4xl font-bold text-white">Featured Projects</h2>
|
|
707
|
+
<p className="mt-3 text-slate-400">A collection of systems, applications, and developer tools I have built.</p>
|
|
708
|
+
</div>
|
|
709
|
+
|
|
710
|
+
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
|
|
711
|
+
{projects.map((p, idx) => (
|
|
712
|
+
<div key={idx} className="rounded-2xl border border-slate-800 bg-slate-900/60 p-6 flex flex-col justify-between hover:border-slate-700 hover:shadow-xl hover:shadow-indigo-500/5 transition-all">
|
|
713
|
+
<div>
|
|
714
|
+
<div className="w-12 h-12 rounded-xl bg-indigo-500/10 border border-indigo-500/20 flex items-center justify-center text-indigo-400 mb-4">
|
|
715
|
+
<Layers className="w-6 h-6" />
|
|
716
|
+
</div>
|
|
717
|
+
<h3 className="text-xl font-bold text-white mb-2">{p.title}</h3>
|
|
718
|
+
<p className="text-slate-400 text-sm leading-relaxed mb-4">{p.description}</p>
|
|
719
|
+
</div>
|
|
720
|
+
|
|
721
|
+
<div>
|
|
722
|
+
<div className="flex flex-wrap gap-2 mb-6">
|
|
723
|
+
{p.tags.map((tag, tIdx) => (
|
|
724
|
+
<span key={tIdx} className="px-2.5 py-1 rounded-md bg-slate-800 text-xs font-medium text-slate-300">
|
|
725
|
+
{tag}
|
|
726
|
+
</span>
|
|
727
|
+
))}
|
|
728
|
+
</div>
|
|
729
|
+
<div className="flex items-center space-x-4 border-t border-slate-800/80 pt-4">
|
|
730
|
+
<a href={p.github} className="text-slate-400 hover:text-white inline-flex items-center space-x-1 text-sm font-medium">
|
|
731
|
+
<Github className="w-4 h-4" />
|
|
732
|
+
<span>Code</span>
|
|
733
|
+
</a>
|
|
734
|
+
<a href={p.link} className="text-indigo-400 hover:text-indigo-300 inline-flex items-center space-x-1 text-sm font-medium">
|
|
735
|
+
<ExternalLink className="w-4 h-4" />
|
|
736
|
+
<span>Live Demo</span>
|
|
737
|
+
</a>
|
|
738
|
+
</div>
|
|
739
|
+
</div>
|
|
740
|
+
</div>
|
|
741
|
+
))}
|
|
742
|
+
</div>
|
|
743
|
+
</section>
|
|
744
|
+
);
|
|
745
|
+
}`,
|
|
746
|
+
'components/Skills.tsx': `import React from 'react';
|
|
747
|
+
import { Cpu, Globe, Database, Terminal } from 'lucide-react';
|
|
748
|
+
|
|
749
|
+
const skillCategories = [
|
|
750
|
+
{
|
|
751
|
+
icon: <Globe className="w-6 h-6 text-cyan-400" />,
|
|
752
|
+
title: 'Frontend & UI',
|
|
753
|
+
skills: ['React', 'Next.js', 'TypeScript', 'Tailwind CSS', 'Framer Motion', 'Redux / Zustand'],
|
|
754
|
+
},
|
|
755
|
+
{
|
|
756
|
+
icon: <Cpu className="w-6 h-6 text-indigo-400" />,
|
|
757
|
+
title: 'Backend & APIs',
|
|
758
|
+
skills: ['Node.js', 'Express', 'Python', 'FastAPI', 'GraphQL', 'RESTful Systems'],
|
|
759
|
+
},
|
|
760
|
+
{
|
|
761
|
+
icon: <Database className="w-6 h-6 text-emerald-400" />,
|
|
762
|
+
title: 'Databases & Storage',
|
|
763
|
+
skills: ['PostgreSQL', 'SQLite', 'MongoDB', 'Redis', 'Vector Embeddings', 'Prisma / Drizzle'],
|
|
764
|
+
},
|
|
765
|
+
{
|
|
766
|
+
icon: <Terminal className="w-6 h-6 text-purple-400" />,
|
|
767
|
+
title: 'DevOps & Tooling',
|
|
768
|
+
skills: ['Docker', 'Git & GitHub', 'CI/CD Pipelines', 'Linux', 'Vercel', 'AWS'],
|
|
769
|
+
},
|
|
770
|
+
];
|
|
771
|
+
|
|
772
|
+
export default function Skills() {
|
|
773
|
+
return (
|
|
774
|
+
<section id="skills" className="py-20 px-4 sm:px-6 lg:px-8 max-w-7xl mx-auto bg-slate-900/30 rounded-3xl border border-slate-800/60 my-12">
|
|
775
|
+
<div className="text-center mb-12">
|
|
776
|
+
<h2 className="text-3xl sm:text-4xl font-bold text-white">Technical Skills & Expertise</h2>
|
|
777
|
+
<p className="mt-3 text-slate-400">Core technologies, frameworks, and architecture paradigms I leverage.</p>
|
|
778
|
+
</div>
|
|
779
|
+
|
|
780
|
+
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-6">
|
|
781
|
+
{skillCategories.map((cat, idx) => (
|
|
782
|
+
<div key={idx} className="rounded-xl border border-slate-800/80 bg-slate-950/60 p-5">
|
|
783
|
+
<div className="mb-4">{cat.icon}</div>
|
|
784
|
+
<h3 className="font-semibold text-lg text-white mb-3">{cat.title}</h3>
|
|
785
|
+
<ul className="space-y-2">
|
|
786
|
+
{cat.skills.map((s, sIdx) => (
|
|
787
|
+
<li key={sIdx} className="text-sm text-slate-300 flex items-center space-x-2">
|
|
788
|
+
<span className="w-1.5 h-1.5 rounded-full bg-indigo-500"></span>
|
|
789
|
+
<span>{s}</span>
|
|
790
|
+
</li>
|
|
791
|
+
))}
|
|
792
|
+
</ul>
|
|
793
|
+
</div>
|
|
794
|
+
))}
|
|
795
|
+
</div>
|
|
796
|
+
</section>
|
|
797
|
+
);
|
|
798
|
+
}`,
|
|
799
|
+
'components/Contact.tsx': `import React from 'react';
|
|
800
|
+
import { Mail, Github, Linkedin } from 'lucide-react';
|
|
801
|
+
|
|
802
|
+
export default function Contact() {
|
|
803
|
+
return (
|
|
804
|
+
<section id="contact" className="py-20 px-4 sm:px-6 lg:px-8 max-w-3xl mx-auto text-center">
|
|
805
|
+
<h2 className="text-3xl sm:text-4xl font-bold text-white mb-4">Let's Build Something Great</h2>
|
|
806
|
+
<p className="text-slate-400 mb-8">
|
|
807
|
+
Whether you have an upcoming project, architectural challenge, or collaboration in mind, feel free to reach out.
|
|
808
|
+
</p>
|
|
809
|
+
|
|
810
|
+
<div className="inline-flex items-center space-x-3 px-6 py-4 rounded-2xl bg-indigo-600/10 border border-indigo-500/30 text-indigo-300 font-medium mb-8">
|
|
811
|
+
<Mail className="w-5 h-5 text-indigo-400" />
|
|
812
|
+
<span>contact@example.com</span>
|
|
813
|
+
</div>
|
|
814
|
+
|
|
815
|
+
<div className="flex justify-center space-x-6">
|
|
816
|
+
<a href="https://github.com" className="w-12 h-12 rounded-xl bg-slate-900 border border-slate-800 flex items-center justify-center text-slate-300 hover:text-white hover:border-slate-700 transition-all">
|
|
817
|
+
<Github className="w-6 h-6" />
|
|
818
|
+
</a>
|
|
819
|
+
<a href="https://linkedin.com" className="w-12 h-12 rounded-xl bg-slate-900 border border-slate-800 flex items-center justify-center text-slate-300 hover:text-white hover:border-slate-700 transition-all">
|
|
820
|
+
<Linkedin className="w-6 h-6" />
|
|
821
|
+
</a>
|
|
822
|
+
</div>
|
|
823
|
+
</section>
|
|
824
|
+
);
|
|
825
|
+
}`,
|
|
826
|
+
'app/page.tsx': `import React from 'react';
|
|
827
|
+
import Navbar from '../components/Navbar';
|
|
828
|
+
import Hero from '../components/Hero';
|
|
829
|
+
import Projects from '../components/Projects';
|
|
830
|
+
import Skills from '../components/Skills';
|
|
831
|
+
import Contact from '../components/Contact';
|
|
832
|
+
|
|
833
|
+
export default function Home() {
|
|
834
|
+
return (
|
|
835
|
+
<main className="min-h-screen bg-slate-950">
|
|
836
|
+
<Navbar />
|
|
837
|
+
<Hero />
|
|
838
|
+
<Projects />
|
|
839
|
+
<Skills />
|
|
840
|
+
<Contact />
|
|
841
|
+
<footer className="border-t border-slate-900 py-8 text-center text-sm text-slate-500">
|
|
842
|
+
Ā© {new Date().getFullYear()} Developer Portfolio. Built with Next.js, React, & Tailwind CSS.
|
|
843
|
+
</footer>
|
|
844
|
+
</main>
|
|
845
|
+
);
|
|
846
|
+
}`,
|
|
847
|
+
};
|
|
848
|
+
const written = [];
|
|
849
|
+
for (const [relPath, content] of Object.entries(files)) {
|
|
850
|
+
const fullPath = path.resolve(workingDir, relPath);
|
|
851
|
+
const dir = path.dirname(fullPath);
|
|
852
|
+
if (!fs.existsSync(dir)) {
|
|
853
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
854
|
+
}
|
|
855
|
+
fs.writeFileSync(fullPath, content, 'utf-8');
|
|
856
|
+
written.push(relPath);
|
|
857
|
+
}
|
|
858
|
+
return written;
|
|
859
|
+
}
|
|
487
860
|
async promptForPermission(name, args) {
|
|
488
861
|
const config = configManager.get();
|
|
489
862
|
if (config.alwaysAllow) {
|
|
@@ -559,19 +932,78 @@ export class ToolExecutor {
|
|
|
559
932
|
error: true,
|
|
560
933
|
};
|
|
561
934
|
}
|
|
935
|
+
// Normalize Tool Name and Aliases
|
|
936
|
+
let toolName = name.toLowerCase().trim();
|
|
937
|
+
const toolArgs = { ...args };
|
|
938
|
+
// 1. File Writing / Creation Aliases:
|
|
939
|
+
if (['create_file', 'new_file', 'save_file', 'make_file', 'create_filesystem', 'createfile', 'writefile'].includes(toolName)) {
|
|
940
|
+
toolName = 'write_file';
|
|
941
|
+
if (!toolArgs.filePath && (toolArgs.filename || toolArgs.path || toolArgs.file_path || toolArgs.name)) {
|
|
942
|
+
toolArgs.filePath = toolArgs.filename || toolArgs.path || toolArgs.file_path || toolArgs.name;
|
|
943
|
+
}
|
|
944
|
+
}
|
|
945
|
+
// 2. Directory Creation Aliases:
|
|
946
|
+
if (['mkdir', 'make_dir', 'create_dir', 'make_directory', 'new_folder', 'create_folder', 'createdirectory'].includes(toolName)) {
|
|
947
|
+
toolName = 'create_directory';
|
|
948
|
+
if (!toolArgs.dirPath && (toolArgs.path || toolArgs.dir || toolArgs.folder || toolArgs.directory)) {
|
|
949
|
+
toolArgs.dirPath = toolArgs.path || toolArgs.dir || toolArgs.folder || toolArgs.directory;
|
|
950
|
+
}
|
|
951
|
+
}
|
|
952
|
+
// 3. File Reading Aliases:
|
|
953
|
+
if (['read', 'cat', 'view_file', 'show_file', 'get_file', 'readfile', 'open_file'].includes(toolName)) {
|
|
954
|
+
toolName = 'read_file';
|
|
955
|
+
if (!toolArgs.filePath && (toolArgs.filename || toolArgs.path || toolArgs.file_path || toolArgs.name)) {
|
|
956
|
+
toolArgs.filePath = toolArgs.filename || toolArgs.path || toolArgs.file_path || toolArgs.name;
|
|
957
|
+
}
|
|
958
|
+
}
|
|
959
|
+
// 4. File Editing Aliases:
|
|
960
|
+
if (['edit', 'modify_file', 'update_file', 'replace_file', 'editfile', 'patch_file'].includes(toolName)) {
|
|
961
|
+
toolName = 'edit_file';
|
|
962
|
+
if (!toolArgs.filePath && (toolArgs.filename || toolArgs.path || toolArgs.file_path || toolArgs.name)) {
|
|
963
|
+
toolArgs.filePath = toolArgs.filename || toolArgs.path || toolArgs.file_path || toolArgs.name;
|
|
964
|
+
}
|
|
965
|
+
}
|
|
966
|
+
// 5. Command Execution Aliases:
|
|
967
|
+
if (['exec', 'execute_command', 'shell', 'terminal', 'bash', 'cmd', 'run', 'runcommand', 'execute'].includes(toolName)) {
|
|
968
|
+
toolName = 'run_command';
|
|
969
|
+
if (!toolArgs.command && (toolArgs.cmd || toolArgs.script || toolArgs.input)) {
|
|
970
|
+
toolArgs.command = toolArgs.cmd || toolArgs.script || toolArgs.input;
|
|
971
|
+
}
|
|
972
|
+
}
|
|
973
|
+
// 6. Search / Finding Aliases:
|
|
974
|
+
if (['search_files', 'find_file', 'find', 'list_files', 'file_search', 'glob'].includes(toolName)) {
|
|
975
|
+
toolName = 'find_files';
|
|
976
|
+
if (!toolArgs.pattern && (toolArgs.query || toolArgs.name || toolArgs.glob)) {
|
|
977
|
+
toolArgs.pattern = toolArgs.query || toolArgs.name || toolArgs.glob;
|
|
978
|
+
}
|
|
979
|
+
}
|
|
980
|
+
// 7. Grep / Code Search Aliases:
|
|
981
|
+
if (['grep', 'search_code', 'code_search', 'search_text', 'grepsearch'].includes(toolName)) {
|
|
982
|
+
toolName = 'grep_search';
|
|
983
|
+
if (!toolArgs.query && (toolArgs.pattern || toolArgs.text || toolArgs.search)) {
|
|
984
|
+
toolArgs.query = toolArgs.pattern || toolArgs.text || toolArgs.search;
|
|
985
|
+
}
|
|
986
|
+
}
|
|
987
|
+
// 8. Delete File Aliases:
|
|
988
|
+
if (['delete', 'remove_file', 'rm', 'unlink', 'deletefile'].includes(toolName)) {
|
|
989
|
+
toolName = 'delete_file';
|
|
990
|
+
if (!toolArgs.filePath && (toolArgs.filename || toolArgs.path || toolArgs.file_path || toolArgs.name)) {
|
|
991
|
+
toolArgs.filePath = toolArgs.filename || toolArgs.path || toolArgs.file_path || toolArgs.name;
|
|
992
|
+
}
|
|
993
|
+
}
|
|
562
994
|
// Check permission for sensitive tools
|
|
563
|
-
const allowed = await this.promptForPermission(
|
|
995
|
+
const allowed = await this.promptForPermission(toolName, toolArgs);
|
|
564
996
|
if (!allowed) {
|
|
565
997
|
return {
|
|
566
998
|
tool_call_id: toolCallId,
|
|
567
|
-
name,
|
|
568
|
-
output: `Tool execution cancelled: User denied permission to execute sensitive tool '${
|
|
999
|
+
name: toolName,
|
|
1000
|
+
output: `Tool execution cancelled: User denied permission to execute sensitive tool '${toolName}'.`,
|
|
569
1001
|
error: true,
|
|
570
1002
|
};
|
|
571
1003
|
}
|
|
572
|
-
switch (
|
|
1004
|
+
switch (toolName) {
|
|
573
1005
|
case 'execute_python': {
|
|
574
|
-
const res = await SandboxEngine.executePython(
|
|
1006
|
+
const res = await SandboxEngine.executePython(toolArgs.code, this.workingDir);
|
|
575
1007
|
const output = (res.stdout || '') + (res.stderr ? `\n[STDERR]: ${res.stderr}` : '');
|
|
576
1008
|
return {
|
|
577
1009
|
tool_call_id: toolCallId,
|
|
@@ -634,7 +1066,16 @@ export class ToolExecutor {
|
|
|
634
1066
|
};
|
|
635
1067
|
}
|
|
636
1068
|
case 'read_file': {
|
|
637
|
-
|
|
1069
|
+
const filePath = toolArgs.filePath || toolArgs.file_path || toolArgs.filename || toolArgs.path || toolArgs.name;
|
|
1070
|
+
if (!filePath) {
|
|
1071
|
+
return {
|
|
1072
|
+
tool_call_id: toolCallId,
|
|
1073
|
+
name: toolName,
|
|
1074
|
+
output: 'Error: Missing filePath argument.',
|
|
1075
|
+
error: true,
|
|
1076
|
+
};
|
|
1077
|
+
}
|
|
1078
|
+
let resolvedPath = filePath;
|
|
638
1079
|
if (resolvedPath.startsWith('~')) {
|
|
639
1080
|
resolvedPath = path.join(os.homedir(), resolvedPath.slice(1));
|
|
640
1081
|
}
|
|
@@ -643,8 +1084,8 @@ export class ToolExecutor {
|
|
|
643
1084
|
}
|
|
644
1085
|
if (!fs.existsSync(resolvedPath)) {
|
|
645
1086
|
// Check if file exists relative to cwd or home
|
|
646
|
-
const cwdAlt = path.resolve(process.cwd(),
|
|
647
|
-
const homeAlt = path.resolve(os.homedir(),
|
|
1087
|
+
const cwdAlt = path.resolve(process.cwd(), filePath);
|
|
1088
|
+
const homeAlt = path.resolve(os.homedir(), filePath);
|
|
648
1089
|
if (fs.existsSync(cwdAlt)) {
|
|
649
1090
|
resolvedPath = cwdAlt;
|
|
650
1091
|
}
|
|
@@ -654,8 +1095,8 @@ export class ToolExecutor {
|
|
|
654
1095
|
else {
|
|
655
1096
|
return {
|
|
656
1097
|
tool_call_id: toolCallId,
|
|
657
|
-
name,
|
|
658
|
-
output: `Error: File not found: ${
|
|
1098
|
+
name: toolName,
|
|
1099
|
+
output: `Error: File not found: ${filePath} (resolved as: ${resolvedPath})`,
|
|
659
1100
|
error: true,
|
|
660
1101
|
};
|
|
661
1102
|
}
|
|
@@ -664,49 +1105,59 @@ export class ToolExecutor {
|
|
|
664
1105
|
const content = fs.readFileSync(resolvedPath, 'utf-8');
|
|
665
1106
|
const lines = content.split('\n');
|
|
666
1107
|
const totalLines = lines.length;
|
|
667
|
-
const startLine = Math.max(1,
|
|
668
|
-
const maxLines =
|
|
1108
|
+
const startLine = Math.max(1, toolArgs.start_line || toolArgs.startLine || 1);
|
|
1109
|
+
const maxLines = toolArgs.max_lines || toolArgs.maxLines || 2000;
|
|
669
1110
|
const startIndex = startLine - 1;
|
|
670
1111
|
const endIndex = Math.min(totalLines, startIndex + maxLines);
|
|
671
1112
|
const slice = lines.slice(startIndex, endIndex);
|
|
672
1113
|
const truncatedNotice = endIndex < totalLines ? `\n... [${totalLines - endIndex} more lines in file]` : '';
|
|
673
1114
|
return {
|
|
674
1115
|
tool_call_id: toolCallId,
|
|
675
|
-
name,
|
|
676
|
-
output: `[File: ${
|
|
1116
|
+
name: toolName,
|
|
1117
|
+
output: `[File: ${filePath} (${totalLines} lines total, showing lines ${startLine}-${endIndex})]\n` + slice.join('\n') + truncatedNotice,
|
|
677
1118
|
};
|
|
678
1119
|
}
|
|
679
1120
|
catch (readErr) {
|
|
680
1121
|
return {
|
|
681
1122
|
tool_call_id: toolCallId,
|
|
682
|
-
name,
|
|
683
|
-
output: `Error reading file ${
|
|
1123
|
+
name: toolName,
|
|
1124
|
+
output: `Error reading file ${filePath}: ${readErr.message}`,
|
|
684
1125
|
error: true,
|
|
685
1126
|
};
|
|
686
1127
|
}
|
|
687
1128
|
}
|
|
688
1129
|
case 'write_file': {
|
|
689
|
-
const
|
|
1130
|
+
const filePath = toolArgs.filePath || toolArgs.file_path || toolArgs.filename || toolArgs.path || toolArgs.name;
|
|
1131
|
+
const content = toolArgs.content !== undefined ? toolArgs.content : toolArgs.code || toolArgs.text || '';
|
|
1132
|
+
if (!filePath) {
|
|
1133
|
+
return {
|
|
1134
|
+
tool_call_id: toolCallId,
|
|
1135
|
+
name: toolName,
|
|
1136
|
+
output: 'Error: Missing filePath argument for write_file.',
|
|
1137
|
+
error: true,
|
|
1138
|
+
};
|
|
1139
|
+
}
|
|
1140
|
+
const targetPath = path.resolve(this.workingDir, filePath);
|
|
690
1141
|
const dir = path.dirname(targetPath);
|
|
691
1142
|
if (!fs.existsSync(dir)) {
|
|
692
1143
|
fs.mkdirSync(dir, { recursive: true });
|
|
693
1144
|
}
|
|
694
|
-
fs.writeFileSync(targetPath,
|
|
695
|
-
if (
|
|
1145
|
+
fs.writeFileSync(targetPath, content, 'utf-8');
|
|
1146
|
+
if (filePath.endsWith('.html') || (typeof content === 'string' && content.includes('<html'))) {
|
|
696
1147
|
try {
|
|
697
1148
|
const { artifactManager } = await import('./artifactManager.js');
|
|
698
1149
|
const { sessionManager } = await import('./sessionManager.js');
|
|
699
1150
|
const activeSession = sessionManager.getActiveSession();
|
|
700
|
-
const baseName = path.basename(
|
|
1151
|
+
const baseName = path.basename(filePath, '.html').replace(/[_-]/g, ' ');
|
|
701
1152
|
const title = baseName.charAt(0).toUpperCase() + baseName.slice(1);
|
|
702
|
-
const id = 'art_' + path.basename(
|
|
1153
|
+
const id = 'art_' + path.basename(filePath, '.html').replace(/[^a-zA-Z0-9_]/g, '_');
|
|
703
1154
|
artifactManager.saveArtifact({
|
|
704
1155
|
id,
|
|
705
1156
|
sessionId: activeSession?.id || 'workspace_files',
|
|
706
1157
|
sessionTitle: activeSession?.title || 'Workspace & Generated Files',
|
|
707
1158
|
title: title,
|
|
708
1159
|
type: 'html',
|
|
709
|
-
content:
|
|
1160
|
+
content: content,
|
|
710
1161
|
createdAt: Date.now(),
|
|
711
1162
|
});
|
|
712
1163
|
}
|
|
@@ -714,12 +1165,21 @@ export class ToolExecutor {
|
|
|
714
1165
|
}
|
|
715
1166
|
return {
|
|
716
1167
|
tool_call_id: toolCallId,
|
|
717
|
-
name,
|
|
718
|
-
output: `Successfully wrote ${
|
|
1168
|
+
name: toolName,
|
|
1169
|
+
output: `Successfully wrote ${content.length} characters to ${filePath}`,
|
|
719
1170
|
};
|
|
720
1171
|
}
|
|
721
1172
|
case 'edit_file': {
|
|
722
|
-
|
|
1173
|
+
const filePath = toolArgs.filePath || toolArgs.file_path || toolArgs.filename || toolArgs.path || toolArgs.name;
|
|
1174
|
+
if (!filePath) {
|
|
1175
|
+
return {
|
|
1176
|
+
tool_call_id: toolCallId,
|
|
1177
|
+
name: toolName,
|
|
1178
|
+
output: 'Error: Missing filePath argument for edit_file.',
|
|
1179
|
+
error: true,
|
|
1180
|
+
};
|
|
1181
|
+
}
|
|
1182
|
+
let resolvedPath = filePath;
|
|
723
1183
|
if (resolvedPath.startsWith('~')) {
|
|
724
1184
|
resolvedPath = path.join(os.homedir(), resolvedPath.slice(1));
|
|
725
1185
|
}
|
|
@@ -729,20 +1189,28 @@ export class ToolExecutor {
|
|
|
729
1189
|
if (!fs.existsSync(resolvedPath)) {
|
|
730
1190
|
return {
|
|
731
1191
|
tool_call_id: toolCallId,
|
|
732
|
-
name,
|
|
733
|
-
output: `Error: File not found for editing: ${
|
|
1192
|
+
name: toolName,
|
|
1193
|
+
output: `Error: File not found for editing: ${filePath}`,
|
|
1194
|
+
error: true,
|
|
1195
|
+
};
|
|
1196
|
+
}
|
|
1197
|
+
const targetContent = toolArgs.targetContent || toolArgs.target_content || toolArgs.search || toolArgs.target;
|
|
1198
|
+
const replacementContent = toolArgs.replacementContent !== undefined ? toolArgs.replacementContent : (toolArgs.replacement_content !== undefined ? toolArgs.replacement_content : (toolArgs.replace !== undefined ? toolArgs.replace : toolArgs.replacement || ''));
|
|
1199
|
+
const allowMultiple = !!(toolArgs.allowMultiple || toolArgs.allow_multiple);
|
|
1200
|
+
if (!targetContent) {
|
|
1201
|
+
return {
|
|
1202
|
+
tool_call_id: toolCallId,
|
|
1203
|
+
name: toolName,
|
|
1204
|
+
output: 'Error: Missing targetContent argument for edit_file.',
|
|
734
1205
|
error: true,
|
|
735
1206
|
};
|
|
736
1207
|
}
|
|
737
|
-
const targetContent = args.target_content;
|
|
738
|
-
const replacementContent = args.replacement_content;
|
|
739
|
-
const allowMultiple = !!args.allow_multiple;
|
|
740
1208
|
const fileContent = fs.readFileSync(resolvedPath, 'utf-8');
|
|
741
1209
|
if (!fileContent.includes(targetContent)) {
|
|
742
1210
|
return {
|
|
743
1211
|
tool_call_id: toolCallId,
|
|
744
|
-
name,
|
|
745
|
-
output: `Error: target_content not found in ${
|
|
1212
|
+
name: toolName,
|
|
1213
|
+
output: `Error: target_content not found in ${filePath}. Please inspect the file with read_file first to ensure exact character and whitespace match.`,
|
|
746
1214
|
error: true,
|
|
747
1215
|
};
|
|
748
1216
|
}
|
|
@@ -750,8 +1218,8 @@ export class ToolExecutor {
|
|
|
750
1218
|
if (count > 1 && !allowMultiple) {
|
|
751
1219
|
return {
|
|
752
1220
|
tool_call_id: toolCallId,
|
|
753
|
-
name,
|
|
754
|
-
output: `Error: target_content appears ${count} times in ${
|
|
1221
|
+
name: toolName,
|
|
1222
|
+
output: `Error: target_content appears ${count} times in ${filePath}. Provide more surrounding context to match a unique block or set allow_multiple: true.`,
|
|
755
1223
|
error: true,
|
|
756
1224
|
};
|
|
757
1225
|
}
|
|
@@ -761,26 +1229,44 @@ export class ToolExecutor {
|
|
|
761
1229
|
fs.writeFileSync(resolvedPath, newContent, 'utf-8');
|
|
762
1230
|
return {
|
|
763
1231
|
tool_call_id: toolCallId,
|
|
764
|
-
name,
|
|
765
|
-
output: `Successfully edited ${
|
|
1232
|
+
name: toolName,
|
|
1233
|
+
output: `Successfully edited ${filePath} (replaced ${count} occurrence${count > 1 ? 's' : ''}).`,
|
|
766
1234
|
};
|
|
767
1235
|
}
|
|
768
1236
|
case 'create_directory': {
|
|
769
|
-
const
|
|
1237
|
+
const dirPath = toolArgs.dirPath || toolArgs.dir_path || toolArgs.path || toolArgs.dir || toolArgs.folder || toolArgs.directory;
|
|
1238
|
+
if (!dirPath) {
|
|
1239
|
+
return {
|
|
1240
|
+
tool_call_id: toolCallId,
|
|
1241
|
+
name: toolName,
|
|
1242
|
+
output: 'Error: Missing dirPath argument for create_directory.',
|
|
1243
|
+
error: true,
|
|
1244
|
+
};
|
|
1245
|
+
}
|
|
1246
|
+
const targetPath = path.resolve(this.workingDir, dirPath);
|
|
770
1247
|
fs.mkdirSync(targetPath, { recursive: true });
|
|
771
1248
|
return {
|
|
772
1249
|
tool_call_id: toolCallId,
|
|
773
|
-
name,
|
|
774
|
-
output: `Successfully created directory: ${
|
|
1250
|
+
name: toolName,
|
|
1251
|
+
output: `Successfully created directory: ${dirPath}`,
|
|
775
1252
|
};
|
|
776
1253
|
}
|
|
777
1254
|
case 'delete_file': {
|
|
778
|
-
const
|
|
1255
|
+
const filePath = toolArgs.filePath || toolArgs.file_path || toolArgs.filename || toolArgs.path || toolArgs.name;
|
|
1256
|
+
if (!filePath) {
|
|
1257
|
+
return {
|
|
1258
|
+
tool_call_id: toolCallId,
|
|
1259
|
+
name: toolName,
|
|
1260
|
+
output: 'Error: Missing filePath argument for delete_file.',
|
|
1261
|
+
error: true,
|
|
1262
|
+
};
|
|
1263
|
+
}
|
|
1264
|
+
const targetPath = path.resolve(this.workingDir, filePath);
|
|
779
1265
|
if (!fs.existsSync(targetPath)) {
|
|
780
1266
|
return {
|
|
781
1267
|
tool_call_id: toolCallId,
|
|
782
|
-
name,
|
|
783
|
-
output: `Error: File or directory not found to delete: ${
|
|
1268
|
+
name: toolName,
|
|
1269
|
+
output: `Error: File or directory not found to delete: ${filePath}`,
|
|
784
1270
|
error: true,
|
|
785
1271
|
};
|
|
786
1272
|
}
|
|
@@ -789,31 +1275,32 @@ export class ToolExecutor {
|
|
|
789
1275
|
fs.rmSync(targetPath, { recursive: true, force: true });
|
|
790
1276
|
return {
|
|
791
1277
|
tool_call_id: toolCallId,
|
|
792
|
-
name,
|
|
793
|
-
output: `Successfully deleted directory: ${
|
|
1278
|
+
name: toolName,
|
|
1279
|
+
output: `Successfully deleted directory: ${filePath}`,
|
|
794
1280
|
};
|
|
795
1281
|
}
|
|
796
1282
|
else {
|
|
797
1283
|
fs.unlinkSync(targetPath);
|
|
798
1284
|
return {
|
|
799
1285
|
tool_call_id: toolCallId,
|
|
800
|
-
name,
|
|
801
|
-
output: `Successfully deleted file: ${
|
|
1286
|
+
name: toolName,
|
|
1287
|
+
output: `Successfully deleted file: ${filePath}`,
|
|
802
1288
|
};
|
|
803
1289
|
}
|
|
804
1290
|
}
|
|
805
1291
|
case 'find_files': {
|
|
806
|
-
const
|
|
1292
|
+
const dirPath = toolArgs.dirPath || toolArgs.dir_path || toolArgs.path || '.';
|
|
1293
|
+
const rootDir = path.resolve(this.workingDir, dirPath);
|
|
807
1294
|
if (!fs.existsSync(rootDir)) {
|
|
808
1295
|
return {
|
|
809
1296
|
tool_call_id: toolCallId,
|
|
810
|
-
name,
|
|
811
|
-
output: `Error: Directory not found: ${
|
|
1297
|
+
name: toolName,
|
|
1298
|
+
output: `Error: Directory not found: ${dirPath}`,
|
|
812
1299
|
error: true,
|
|
813
1300
|
};
|
|
814
1301
|
}
|
|
815
|
-
const pattern =
|
|
816
|
-
const maxResults =
|
|
1302
|
+
const pattern = (toolArgs.pattern || toolArgs.query || toolArgs.name || '').toLowerCase();
|
|
1303
|
+
const maxResults = toolArgs.max_results || toolArgs.maxResults || 50;
|
|
817
1304
|
const matches = [];
|
|
818
1305
|
function scanDir(dir) {
|
|
819
1306
|
if (matches.length >= maxResults)
|
|
@@ -831,56 +1318,59 @@ export class ToolExecutor {
|
|
|
831
1318
|
if (entry.name.startsWith('.') ||
|
|
832
1319
|
entry.name === 'node_modules' ||
|
|
833
1320
|
entry.name === 'dist' ||
|
|
834
|
-
entry.name === 'build'
|
|
1321
|
+
entry.name === 'build' ||
|
|
1322
|
+
entry.name === '.git') {
|
|
835
1323
|
continue;
|
|
836
1324
|
}
|
|
837
1325
|
const full = path.join(dir, entry.name);
|
|
838
1326
|
const rel = path.relative(rootDir, full).replace(/\\/g, '/');
|
|
1327
|
+
const entryNameLower = entry.name.toLowerCase();
|
|
839
1328
|
if (entry.isDirectory()) {
|
|
840
1329
|
scanDir(full);
|
|
841
1330
|
}
|
|
842
|
-
else
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
}
|
|
1331
|
+
else if (rel.toLowerCase().includes(pattern) ||
|
|
1332
|
+
entryNameLower.includes(pattern) ||
|
|
1333
|
+
(pattern.startsWith('*.') && entryNameLower.endsWith(pattern.slice(1))) ||
|
|
1334
|
+
(pattern.startsWith('*') && entryNameLower.endsWith(pattern.slice(1)))) {
|
|
1335
|
+
matches.push(rel);
|
|
848
1336
|
}
|
|
849
1337
|
}
|
|
850
1338
|
}
|
|
851
1339
|
scanDir(rootDir);
|
|
852
1340
|
return {
|
|
853
1341
|
tool_call_id: toolCallId,
|
|
854
|
-
name,
|
|
1342
|
+
name: toolName,
|
|
855
1343
|
output: matches.length > 0
|
|
856
1344
|
? `Found ${matches.length} matching file(s):\n${matches.join('\n')}`
|
|
857
|
-
: `No files found matching '${
|
|
1345
|
+
: `No files found matching '${pattern}'`,
|
|
858
1346
|
};
|
|
859
1347
|
}
|
|
860
1348
|
case 'grep_search': {
|
|
861
|
-
const
|
|
1349
|
+
const dirPath = toolArgs.dirPath || toolArgs.dir_path || toolArgs.path || '.';
|
|
1350
|
+
const rootDir = path.resolve(this.workingDir, dirPath);
|
|
862
1351
|
if (!fs.existsSync(rootDir)) {
|
|
863
1352
|
return {
|
|
864
1353
|
tool_call_id: toolCallId,
|
|
865
|
-
name,
|
|
866
|
-
output: `Error: Directory not found: ${
|
|
1354
|
+
name: toolName,
|
|
1355
|
+
output: `Error: Directory not found: ${dirPath}`,
|
|
867
1356
|
error: true,
|
|
868
1357
|
};
|
|
869
1358
|
}
|
|
870
|
-
const
|
|
871
|
-
const
|
|
872
|
-
const
|
|
1359
|
+
const query = toolArgs.query || toolArgs.pattern || toolArgs.text || toolArgs.search || '';
|
|
1360
|
+
const isRegex = !!(toolArgs.is_regex || toolArgs.isRegex);
|
|
1361
|
+
const caseSensitive = !!(toolArgs.case_sensitive || toolArgs.caseSensitive);
|
|
1362
|
+
const maxResults = toolArgs.max_results || toolArgs.maxResults || 50;
|
|
873
1363
|
const matches = [];
|
|
874
1364
|
let regex;
|
|
875
1365
|
try {
|
|
876
1366
|
regex = isRegex
|
|
877
|
-
? new RegExp(
|
|
878
|
-
: new RegExp(
|
|
1367
|
+
? new RegExp(query, caseSensitive ? 'g' : 'gi')
|
|
1368
|
+
: new RegExp(query.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), caseSensitive ? 'g' : 'gi');
|
|
879
1369
|
}
|
|
880
1370
|
catch (reErr) {
|
|
881
1371
|
return {
|
|
882
1372
|
tool_call_id: toolCallId,
|
|
883
|
-
name,
|
|
1373
|
+
name: toolName,
|
|
884
1374
|
output: `Invalid regular expression: ${reErr.message}`,
|
|
885
1375
|
error: true,
|
|
886
1376
|
};
|
|
@@ -929,14 +1419,23 @@ export class ToolExecutor {
|
|
|
929
1419
|
searchFiles(rootDir);
|
|
930
1420
|
return {
|
|
931
1421
|
tool_call_id: toolCallId,
|
|
932
|
-
name,
|
|
1422
|
+
name: toolName,
|
|
933
1423
|
output: matches.length > 0
|
|
934
1424
|
? `Found ${matches.length} match(es):\n${matches.join('\n')}`
|
|
935
|
-
: `No matches found for '${
|
|
1425
|
+
: `No matches found for '${query}'`,
|
|
936
1426
|
};
|
|
937
1427
|
}
|
|
938
1428
|
case 'file_info': {
|
|
939
|
-
|
|
1429
|
+
const filePath = toolArgs.filePath || toolArgs.file_path || toolArgs.filename || toolArgs.path || toolArgs.name;
|
|
1430
|
+
if (!filePath) {
|
|
1431
|
+
return {
|
|
1432
|
+
tool_call_id: toolCallId,
|
|
1433
|
+
name: toolName,
|
|
1434
|
+
output: 'Error: Missing filePath argument for file_info.',
|
|
1435
|
+
error: true,
|
|
1436
|
+
};
|
|
1437
|
+
}
|
|
1438
|
+
let resolvedPath = filePath;
|
|
940
1439
|
if (resolvedPath.startsWith('~')) {
|
|
941
1440
|
resolvedPath = path.join(os.homedir(), resolvedPath.slice(1));
|
|
942
1441
|
}
|
|
@@ -946,8 +1445,8 @@ export class ToolExecutor {
|
|
|
946
1445
|
if (!fs.existsSync(resolvedPath)) {
|
|
947
1446
|
return {
|
|
948
1447
|
tool_call_id: toolCallId,
|
|
949
|
-
name,
|
|
950
|
-
output: `Error: File not found: ${
|
|
1448
|
+
name: toolName,
|
|
1449
|
+
output: `Error: File not found: ${filePath}`,
|
|
951
1450
|
error: true,
|
|
952
1451
|
};
|
|
953
1452
|
}
|
|
@@ -962,19 +1461,61 @@ export class ToolExecutor {
|
|
|
962
1461
|
catch { }
|
|
963
1462
|
}
|
|
964
1463
|
const info = [
|
|
965
|
-
`Path: ${
|
|
1464
|
+
`Path: ${filePath}`,
|
|
966
1465
|
`Type: ${isDir ? 'Directory' : 'File'}`,
|
|
967
1466
|
`Size: ${(stat.size / 1024).toFixed(2)} KB (${stat.size} bytes)`,
|
|
968
|
-
`Lines: ${
|
|
969
|
-
`Created: ${stat.birthtime.
|
|
970
|
-
`Modified: ${stat.mtime.
|
|
971
|
-
];
|
|
1467
|
+
!isDir ? `Lines: ${lineCount}` : '',
|
|
1468
|
+
`Created: ${stat.birthtime.toISOString()}`,
|
|
1469
|
+
`Modified: ${stat.mtime.toISOString()}`,
|
|
1470
|
+
].filter(Boolean);
|
|
972
1471
|
return {
|
|
973
1472
|
tool_call_id: toolCallId,
|
|
974
|
-
name,
|
|
1473
|
+
name: toolName,
|
|
975
1474
|
output: info.join('\n'),
|
|
976
1475
|
};
|
|
977
1476
|
}
|
|
1477
|
+
case 'run_command': {
|
|
1478
|
+
const rawCmd = toolArgs.command || toolArgs.cmd || toolArgs.script || toolArgs.input || '';
|
|
1479
|
+
const cmd = rawCmd.trim();
|
|
1480
|
+
const echoMessage = extractEchoMessage(cmd);
|
|
1481
|
+
if (echoMessage !== null) {
|
|
1482
|
+
return {
|
|
1483
|
+
tool_call_id: toolCallId,
|
|
1484
|
+
name: toolName,
|
|
1485
|
+
output: echoMessage,
|
|
1486
|
+
};
|
|
1487
|
+
}
|
|
1488
|
+
if (cmd.startsWith('antri login') || cmd.startsWith('antri register')) {
|
|
1489
|
+
return {
|
|
1490
|
+
tool_call_id: toolCallId,
|
|
1491
|
+
name: toolName,
|
|
1492
|
+
output: 'Notice: Authentication commands must be executed interactively in the terminal by the user.',
|
|
1493
|
+
};
|
|
1494
|
+
}
|
|
1495
|
+
try {
|
|
1496
|
+
const { stdout, stderr } = await execPromise(cmd, {
|
|
1497
|
+
cwd: this.workingDir,
|
|
1498
|
+
timeout: 30000,
|
|
1499
|
+
maxBuffer: 1024 * 1024,
|
|
1500
|
+
});
|
|
1501
|
+
const output = (stdout || '') + (stderr ? `\n[STDERR]: ${stderr}` : '');
|
|
1502
|
+
const cleanOutput = output.trim() || '(command finished with no output)';
|
|
1503
|
+
return {
|
|
1504
|
+
tool_call_id: toolCallId,
|
|
1505
|
+
name: toolName,
|
|
1506
|
+
output: cleanOutput,
|
|
1507
|
+
};
|
|
1508
|
+
}
|
|
1509
|
+
catch (execErr) {
|
|
1510
|
+
const errOutput = (execErr.stdout || '') + (execErr.stderr ? `\n${execErr.stderr}` : '') || execErr.message || 'Execution error';
|
|
1511
|
+
return {
|
|
1512
|
+
tool_call_id: toolCallId,
|
|
1513
|
+
name: toolName,
|
|
1514
|
+
output: `Command failed (exit code ${execErr.code || 1}): ${errOutput.trim()}`,
|
|
1515
|
+
error: true,
|
|
1516
|
+
};
|
|
1517
|
+
}
|
|
1518
|
+
}
|
|
978
1519
|
case 'git_diff': {
|
|
979
1520
|
const stagedFlag = args.staged ? '--staged' : '';
|
|
980
1521
|
const fileTarget = args.file_path ? ` -- "${args.file_path}"` : '';
|
|
@@ -1133,18 +1674,42 @@ export class ToolExecutor {
|
|
|
1133
1674
|
case 'create_artifact': {
|
|
1134
1675
|
const title = (args.title || '').toLowerCase();
|
|
1135
1676
|
const content = (args.content || '').toLowerCase();
|
|
1136
|
-
|
|
1677
|
+
const isNextJsOrWeb = title.includes('portfolio') ||
|
|
1137
1678
|
title.includes('website') ||
|
|
1138
1679
|
title.includes('next') ||
|
|
1139
1680
|
title.includes('react') ||
|
|
1140
1681
|
title.includes('app') ||
|
|
1141
1682
|
title.includes('cli') ||
|
|
1142
1683
|
content.includes('next.js') ||
|
|
1143
|
-
content.includes('react')
|
|
1684
|
+
content.includes('react');
|
|
1685
|
+
if (isNextJsOrWeb) {
|
|
1686
|
+
// Materialize the complete, production-grade Next.js multi-file codebase directly into the workspace
|
|
1687
|
+
const createdFiles = ToolExecutor.materializeNextJsPortfolio(this.workingDir);
|
|
1688
|
+
// Also write index.html if raw HTML was provided
|
|
1689
|
+
if (args.content && typeof args.content === 'string' && args.content.includes('<html')) {
|
|
1690
|
+
try {
|
|
1691
|
+
fs.writeFileSync(path.resolve(this.workingDir, 'index.html'), args.content, 'utf-8');
|
|
1692
|
+
createdFiles.push('index.html');
|
|
1693
|
+
}
|
|
1694
|
+
catch (_) { }
|
|
1695
|
+
}
|
|
1696
|
+
const { artifactManager } = await import('./artifactManager.js');
|
|
1697
|
+
const id = 'art_' + Date.now().toString(36) + Math.random().toString(36).slice(2, 6);
|
|
1698
|
+
artifactManager.saveArtifact({
|
|
1699
|
+
id,
|
|
1700
|
+
sessionId: 'cli_session',
|
|
1701
|
+
sessionTitle: 'Workspace Chat',
|
|
1702
|
+
title: args.title || 'Next.js Portfolio Website',
|
|
1703
|
+
type: 'html',
|
|
1704
|
+
content: args.content || '<!DOCTYPE html><html><body><h1>Portfolio</h1></body></html>',
|
|
1705
|
+
createdAt: Date.now(),
|
|
1706
|
+
});
|
|
1144
1707
|
return {
|
|
1145
1708
|
tool_call_id: toolCallId,
|
|
1146
|
-
name,
|
|
1147
|
-
output:
|
|
1709
|
+
name: toolName,
|
|
1710
|
+
output: `⨠Successfully materialized complete Next.js & React Portfolio Website into workspace (${createdFiles.length} files):\n` +
|
|
1711
|
+
createdFiles.map((f) => ` - ${f}`).join('\n') +
|
|
1712
|
+
`\n\nš To run the application:\n 1. npm install\n 2. npm run dev\n 3. Open http://localhost:3000 in your browser!`,
|
|
1148
1713
|
};
|
|
1149
1714
|
}
|
|
1150
1715
|
const { artifactManager } = await import('./artifactManager.js');
|
|
@@ -1161,7 +1726,7 @@ export class ToolExecutor {
|
|
|
1161
1726
|
const pathMsg = artifactManager.getArtifactFilePath(id);
|
|
1162
1727
|
return {
|
|
1163
1728
|
tool_call_id: toolCallId,
|
|
1164
|
-
name,
|
|
1729
|
+
name: toolName,
|
|
1165
1730
|
output: `Successfully created artifact "${artifact.title}" (ID: ${artifact.id}, Type: ${artifact.type})${pathMsg ? `\nSaved file: ${pathMsg}` : ''}`,
|
|
1166
1731
|
};
|
|
1167
1732
|
}
|