knoxis-helper 1.3.1 → 1.3.3

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.
@@ -657,6 +657,23 @@ async function handleRequest(req, res) {
657
657
 
658
658
  // Start pair programming session (opens terminal)
659
659
  if (pathname === '/pair/start' && method === 'POST') {
660
+ // Build CLAUDE.md content from task when backend doesn't provide claudeMdContent
661
+ function buildClaudeMdFromTask(taskText, workspacePath) {
662
+ const lines = [];
663
+ lines.push('# Project Instructions\n');
664
+ lines.push('## Current Task');
665
+ lines.push(taskText);
666
+ lines.push('');
667
+ lines.push('## Working Agreement');
668
+ lines.push('- Work autonomously. Do not ask clarifying questions - make your best engineering judgment and proceed.');
669
+ lines.push('- Read and understand existing code before making changes.');
670
+ lines.push('- Follow existing patterns in the codebase.');
671
+ lines.push('- Keep changes minimal and focused on the task.');
672
+ lines.push('- Verify your work compiles/runs where possible.');
673
+ lines.push('- Be direct and concise in any output.');
674
+ return lines.join('\n');
675
+ }
676
+
660
677
  try {
661
678
  const body = await parseBody(req);
662
679
  const { workspace, task, file, provider, headless, sessionId, claudeMdContent } = body;
@@ -675,18 +692,25 @@ async function handleRequest(req, res) {
675
692
  }
676
693
  }
677
694
 
678
- // Write CLAUDE.md if provided — ensures fresh task context every session
679
- if (claudeMdContent && fs.existsSync(workspaceDir)) {
695
+ // Write CLAUDE.md for supplementary context.
696
+ // If claudeMdContent was provided (from backend relay), use it directly.
697
+ // Otherwise, build it from the task field.
698
+ const effectiveClaudeMd = claudeMdContent || buildClaudeMdFromTask(task, workspace);
699
+ if (effectiveClaudeMd && fs.existsSync(workspaceDir)) {
680
700
  try {
681
- fs.writeFileSync(path.join(workspaceDir, 'CLAUDE.md'), claudeMdContent, 'utf8');
682
- console.log(`📄 Wrote CLAUDE.md to ${workspaceDir}`);
701
+ fs.writeFileSync(path.join(workspaceDir, 'CLAUDE.md'), effectiveClaudeMd, 'utf8');
702
+ console.log(`📄 Wrote CLAUDE.md to ${workspaceDir} (${effectiveClaudeMd.length} chars)`);
683
703
  } catch (e) {
684
704
  console.warn(`⚠️ Failed to write CLAUDE.md: ${e.message}`);
685
705
  }
686
706
  }
687
707
 
688
- // Short command Claude reads CLAUDE.md for full context
689
- const command = `claude --dangerously-skip-permissions "Complete the task described in CLAUDE.md. Work autonomously."`;
708
+ // Write the actual task to a temp file and pipe it to Claude via stdin.
709
+ // This avoids shell escaping issues with quotes/backticks/etc in the task text.
710
+ const promptFile = path.join(os.tmpdir(), `knoxis-task-${sessionId || Date.now()}.txt`);
711
+ const promptText = file ? `Working on file: ${file}\n\nTask: ${task}` : task;
712
+ fs.writeFileSync(promptFile, promptText, 'utf8');
713
+ const command = `cat "${promptFile}" | claude --dangerously-skip-permissions`;
690
714
 
691
715
  if (headless) {
692
716
  const result = await runHeadlessProcess({
@@ -999,35 +1023,39 @@ function connectRelayWebSocket() {
999
1023
  if (msg.type === 'execute_command') {
1000
1024
  console.log(`📥 Relay command received: ${msg.requestId}`);
1001
1025
  console.log(` 📂 Dir: ${msg.workingDir}`);
1002
- console.log(` 📝 Cmd: ${(msg.command || '').substring(0, 100)}...`);
1026
+ console.log(` 📝 Prompt: ${msg.prompt ? msg.prompt.substring(0, 120) + '...' : '(none)'}`);
1003
1027
 
1004
1028
  const workspace = msg.workingDir || process.cwd();
1005
- const command = msg.command || '';
1029
+ const wsDir = workspace.startsWith('~')
1030
+ ? path.join(os.homedir(), workspace.slice(1))
1031
+ : workspace;
1006
1032
 
1007
- // Write CLAUDE.md to workspace before executing ensures Claude auto-reads
1008
- // fresh task context and prevents accumulation of old tasks
1009
- if (msg.claudeMdContent && workspace && workspace !== '~') {
1033
+ // Write CLAUDE.md for supplementary context (developer brief, working agreement, etc.)
1034
+ if (msg.claudeMdContent && fs.existsSync(wsDir)) {
1010
1035
  try {
1011
- const wsDir = workspace.startsWith('~')
1012
- ? path.join(os.homedir(), workspace.slice(1))
1013
- : workspace;
1014
- if (fs.existsSync(wsDir)) {
1015
- const claudeMdPath = path.join(wsDir, 'CLAUDE.md');
1016
- fs.writeFileSync(claudeMdPath, msg.claudeMdContent, 'utf8');
1017
- console.log(` 📄 Wrote CLAUDE.md to ${claudeMdPath} (${msg.claudeMdContent.length} chars)`);
1018
- } else {
1019
- console.warn(` ⚠️ Workspace does not exist, skipping CLAUDE.md write: ${wsDir}`);
1020
- }
1036
+ fs.writeFileSync(path.join(wsDir, 'CLAUDE.md'), msg.claudeMdContent, 'utf8');
1037
+ console.log(` 📄 Wrote CLAUDE.md (${msg.claudeMdContent.length} chars)`);
1021
1038
  } catch (writeErr) {
1022
1039
  console.warn(` ⚠️ Failed to write CLAUDE.md: ${writeErr.message}`);
1023
1040
  }
1024
1041
  }
1025
1042
 
1043
+ // Build the actual command. If a prompt was sent, write it to a temp file
1044
+ // and pipe it to Claude via stdin — avoids all shell escaping issues.
1045
+ let command = msg.command || '';
1046
+ let promptFile = null;
1047
+ if (msg.prompt) {
1048
+ promptFile = path.join(os.tmpdir(), `knoxis-task-${msg.requestId || Date.now()}.txt`);
1049
+ fs.writeFileSync(promptFile, msg.prompt, 'utf8');
1050
+ command = `cat "${promptFile}" | claude --dangerously-skip-permissions`;
1051
+ console.log(` 📝 Task written to ${promptFile} (${msg.prompt.length} chars)`);
1052
+ }
1053
+
1026
1054
  let result;
1027
1055
  try {
1028
1056
  if (msg.headless) {
1029
1057
  result = await runHeadlessProcess({
1030
- workspace,
1058
+ workspace: wsDir,
1031
1059
  command,
1032
1060
  prompt: msg.prompt,
1033
1061
  sessionLabel: msg.requestId || 'relay'
@@ -1035,11 +1063,11 @@ function connectRelayWebSocket() {
1035
1063
  } else {
1036
1064
  const platform = os.platform();
1037
1065
  if (platform === 'darwin') {
1038
- await openMacTerminal(workspace, command);
1066
+ await openMacTerminal(wsDir, command);
1039
1067
  } else if (platform === 'win32') {
1040
- await openWindowsTerminal(workspace, command);
1068
+ await openWindowsTerminal(wsDir, command);
1041
1069
  } else {
1042
- await openLinuxTerminal(workspace, command);
1070
+ await openLinuxTerminal(wsDir, command);
1043
1071
  }
1044
1072
  result = { success: true, message: 'Terminal opened via relay' };
1045
1073
  }
@@ -1047,6 +1075,13 @@ function connectRelayWebSocket() {
1047
1075
  result = { success: false, error: err.message };
1048
1076
  }
1049
1077
 
1078
+ // Clean up temp prompt file (Claude already read it)
1079
+ if (promptFile) {
1080
+ setTimeout(() => {
1081
+ try { fs.unlinkSync(promptFile); } catch (e) {}
1082
+ }, 5000); // Delay to ensure cat has finished piping
1083
+ }
1084
+
1050
1085
  // Send result back to backend
1051
1086
  const response = JSON.stringify({
1052
1087
  type: 'command_result',
@@ -1187,7 +1222,7 @@ server.listen(serverMeta.port, () => {
1187
1222
  const scheme = serverMeta.secure ? 'https' : 'http';
1188
1223
  console.log('');
1189
1224
  console.log('╔══════════════════════════════════════════════════════════════╗');
1190
- console.log('║ 🚀 KNOXIS LOCAL AGENT v2.1.0 (HTTPS + CORS Fix) ║');
1225
+ console.log('║ 🚀 KNOXIS LOCAL AGENT v2.3.0 (Prompt Pipe Fix) ║');
1191
1226
  console.log('╚══════════════════════════════════════════════════════════════╝');
1192
1227
  console.log('');
1193
1228
  console.log(`🔒 Mode: ${serverMeta.secure ? 'HTTPS (Secure)' : 'HTTP (Insecure - see warning below)'}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "knoxis-helper",
3
- "version": "1.3.1",
3
+ "version": "1.3.3",
4
4
  "description": "Local helper for Knoxis pair programming - connects your machine to Knoxis on qig.ai",
5
5
  "bin": {
6
6
  "knoxis-helper": "./bin/knoxis-helper.js"