sapper-iq 1.1.26 → 1.1.28

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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/sapper.mjs +81 -17
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sapper-iq",
3
- "version": "1.1.26",
3
+ "version": "1.1.28",
4
4
  "description": "AI-powered development assistant that executes commands and builds projects",
5
5
  "main": "sapper.mjs",
6
6
  "bin": {
package/sapper.mjs CHANGED
@@ -498,9 +498,9 @@ async function runSapper() {
498
498
 
499
499
  // Quick tips box
500
500
  console.log(box(
501
- `${chalk.yellow('šŸ’”')} Type ${chalk.cyan('/help')} for commands\n` +
501
+ `${chalk.yellow('šŸ’”')} Use ${chalk.cyan('@file')} to attach files (e.g., "fix @app.js")\n` +
502
502
  `${chalk.yellow('šŸ’”')} Type ${chalk.cyan('/scan')} to load entire codebase\n` +
503
- `${chalk.yellow('šŸ’”')} Type ${chalk.cyan('exit')} to quit`,
503
+ `${chalk.yellow('šŸ’”')} Type ${chalk.cyan('/help')} for all commands`,
504
504
  'Quick Tips', 'gray'
505
505
  ));
506
506
  console.log();
@@ -552,13 +552,23 @@ async function runSapper() {
552
552
  if (messages.length === 0) {
553
553
  messages = [{
554
554
  role: 'system',
555
- content: `You are Sapper, an AGENT, You can use tools to take action:
556
- - [TOOL:LIST]path[/TOOL] - List directory
557
- - [TOOL:READ]path[/TOOL] - Read file
558
- - [TOOL:SEARCH]pattern[/TOOL] - Search codebase
559
- - [TOOL:WRITE]path:::content[/TOOL] - Create/overwrite file (use ::: between path and content)
560
- - [TOOL:PATCH]path:::old|||new[/TOOL] - Edit file
561
- - [TOOL:SHELL]command[/TOOL] - Run terminal command`
555
+ content: `You are Sapper, a high-level Autonomous Software Engineer.
556
+ Your goal is to solve the user's request by interacting with the filesystem and shell.
557
+
558
+ RULES:
559
+ 1. EXPLORE FIRST: Use LIST and READ to understand the codebase before making changes.
560
+ 2. THINK IN STEPS: Explain what you found and what you plan to do before executing tools.
561
+ 3. BE PRECISE: When using PATCH, ensure the 'oldText' matches exactly.
562
+ 4. VERIFY: After writing code, use the SHELL tool to run tests or linting.
563
+ 5. NO HALLUCINATIONS: If a file doesn't exist, don't guess its content. List the directory instead.
564
+
565
+ TOOL SYNTAX:
566
+ - [TOOL:LIST]dir[/TOOL] - List directory contents
567
+ - [TOOL:READ]file_path[/TOOL] - Read file contents
568
+ - [TOOL:SEARCH]pattern[/TOOL] - Search codebase for pattern
569
+ - [TOOL:WRITE]path:::content[/TOOL] - Create/overwrite file
570
+ - [TOOL:PATCH]path:::old|||new[/TOOL] - Edit existing file
571
+ - [TOOL:SHELL]command[/TOOL] - Run shell command`
562
572
  }];
563
573
  }
564
574
 
@@ -641,13 +651,22 @@ async function runSapper() {
641
651
  // 4. Add reminder to stay in Agent Mode (not chatbot mode)
642
652
  messages.push({
643
653
  role: 'system',
644
- content: `CONTEXT PRUNED. REMINDER: You are an AGENT, You can use tools to take action:
645
- - [TOOL:LIST]path[/TOOL] - List directory
646
- - [TOOL:READ]path[/TOOL] - Read file
647
- - [TOOL:SEARCH]pattern[/TOOL] - Search codebase
648
- - [TOOL:WRITE]path:::content[/TOOL] - Create/overwrite file (use ::: between path and content)
649
- - [TOOL:PATCH]path:::old|||new[/TOOL] - Edit file
650
- - [TOOL:SHELL]command[/TOOL] - Run terminal command.`
654
+ content: `CONTEXT PRUNED. REMINDER: You are Sapper, an Autonomous Software Engineer.
655
+
656
+ RULES:
657
+ 1. EXPLORE FIRST: Use LIST and READ before making changes.
658
+ 2. THINK IN STEPS: Explain your plan before executing tools.
659
+ 3. BE PRECISE: When using PATCH, ensure 'oldText' matches exactly.
660
+ 4. VERIFY: Run tests or linting after writing code.
661
+ 5. NO HALLUCINATIONS: Don't guess file contents.
662
+
663
+ TOOL SYNTAX:
664
+ - [TOOL:LIST]dir[/TOOL]
665
+ - [TOOL:READ]file_path[/TOOL]
666
+ - [TOOL:SEARCH]pattern[/TOOL]
667
+ - [TOOL:WRITE]path:::content[/TOOL]
668
+ - [TOOL:PATCH]path:::old|||new[/TOOL]
669
+ - [TOOL:SHELL]command[/TOOL]`
651
670
  });
652
671
 
653
672
  // 5. Save to context file so it persists
@@ -662,6 +681,7 @@ async function runSapper() {
662
681
  if (input.toLowerCase() === '/help') {
663
682
  console.log();
664
683
  const helpContent =
684
+ `${chalk.cyan('@file')} ${chalk.gray('│')} Attach file to prompt (e.g., @src/app.js)\n` +
665
685
  `${chalk.cyan('/scan')} ${chalk.gray('│')} Scan codebase into context\n` +
666
686
  `${chalk.cyan('/recall')} ${chalk.gray('│')} Search memory for relevant context\n` +
667
687
  `${chalk.cyan('/reset /clear')} ${chalk.gray('│')} Clear all context\n` +
@@ -770,7 +790,51 @@ async function runSapper() {
770
790
  continue;
771
791
  }
772
792
 
773
- messages.push({ role: 'user', content: input });
793
+ // Process @file attachments in prompt (e.g., "analyze @package.json" or "fix @src/index.js")
794
+ let processedInput = input;
795
+ const fileAttachments = [];
796
+ const attachRegex = /@([\w.\/\-_]+)/g;
797
+ let attachMatch;
798
+
799
+ while ((attachMatch = attachRegex.exec(input)) !== null) {
800
+ const filePath = attachMatch[1];
801
+ try {
802
+ if (fs.existsSync(filePath)) {
803
+ const stats = fs.statSync(filePath);
804
+ if (stats.isFile()) {
805
+ if (stats.size > MAX_FILE_SIZE) {
806
+ console.log(chalk.yellow(`āš ļø @${filePath} is too large (${Math.round(stats.size/1024)}KB), skipping`));
807
+ } else {
808
+ const content = fs.readFileSync(filePath, 'utf8');
809
+ fileAttachments.push({ path: filePath, content, size: stats.size });
810
+ console.log(chalk.green(`šŸ“Ž Attached: ${filePath} (${Math.round(stats.size/1024)}KB)`));
811
+ }
812
+ }
813
+ } else {
814
+ // Not a file - might be an @mention for something else, ignore
815
+ }
816
+ } catch (e) {
817
+ console.log(chalk.yellow(`āš ļø Could not read @${filePath}: ${e.message}`));
818
+ }
819
+ }
820
+
821
+ // Build the final message with attachments
822
+ if (fileAttachments.length > 0) {
823
+ let attachedContent = '\n\n══════════════════════════════════════\n';
824
+ attachedContent += `šŸ“Ž ATTACHED FILES (${fileAttachments.length})\n`;
825
+ attachedContent += '══════════════════════════════════════\n\n';
826
+
827
+ for (const file of fileAttachments) {
828
+ attachedContent += `ā”Œā”€ā”€ā”€ ${file.path} ───\n`;
829
+ attachedContent += file.content;
830
+ if (!file.content.endsWith('\n')) attachedContent += '\n';
831
+ attachedContent += `└─── END ${file.path} ───\n\n`;
832
+ }
833
+
834
+ processedInput = input + attachedContent;
835
+ }
836
+
837
+ messages.push({ role: 'user', content: processedInput });
774
838
 
775
839
  let toolRounds = 0; // Prevent infinite loops
776
840
  const MAX_TOOL_ROUNDS = 20;