codeep 1.2.0 → 1.2.2

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.
@@ -501,32 +501,52 @@ async function runSkill(nameOrShortcut, args) {
501
501
  const params = parseSkillArgs(args.join(' '), skill);
502
502
  app.addMessage({ role: 'user', content: `/${skill.name}${args.length ? ' ' + args.join(' ') : ''}` });
503
503
  trackSkillUsage(skill.name);
504
- const { execSync } = await import('child_process');
504
+ const { spawnSync } = await import('child_process');
505
505
  try {
506
506
  const result = await executeSkill(skill, params, {
507
507
  onCommand: async (cmd) => {
508
+ // Use spawnSync via shell for reliable stdout+stderr capture
509
+ const proc = spawnSync(cmd, {
510
+ cwd: projectPath || process.cwd(),
511
+ encoding: 'utf-8',
512
+ timeout: 60000,
513
+ shell: true,
514
+ stdio: ['pipe', 'pipe', 'pipe'],
515
+ });
516
+ const stdout = (proc.stdout || '').trim();
517
+ const stderr = (proc.stderr || '').trim();
518
+ const output = stdout || stderr || '';
519
+ if (proc.status === 0) {
520
+ if (output) {
521
+ app.addMessage({ role: 'system', content: `\`${cmd}\`\n\`\`\`\n${output}\n\`\`\`` });
522
+ }
523
+ return output;
524
+ }
525
+ // Non-zero exit
526
+ if (output) {
527
+ app.addMessage({ role: 'system', content: `\`${cmd}\` failed:\n\`\`\`\n${output}\n\`\`\`` });
528
+ }
529
+ throw new Error(output || `Command exited with code ${proc.status}`);
530
+ },
531
+ onPrompt: async (prompt) => {
508
532
  try {
509
- const output = execSync(cmd, {
510
- cwd: projectPath || process.cwd(),
511
- encoding: 'utf-8',
512
- timeout: 60000,
513
- });
514
- return output.trim();
533
+ app.addMessage({ role: 'user', content: prompt });
534
+ app.startStreaming();
535
+ const history = app.getChatHistory();
536
+ const response = await chat(prompt, history, (chunk) => {
537
+ app.addStreamChunk(chunk);
538
+ }, undefined, projectContext, undefined);
539
+ app.endStreaming();
540
+ // Return the AI response text for use in subsequent steps
541
+ const lastMsg = app.getMessages();
542
+ const assistantMsg = lastMsg[lastMsg.length - 1];
543
+ return (assistantMsg?.role === 'assistant' ? assistantMsg.content : response || '').trim();
515
544
  }
516
545
  catch (err) {
517
- const error = err;
518
- throw new Error(error.stderr || error.message || 'Command failed');
546
+ app.endStreaming();
547
+ throw err;
519
548
  }
520
549
  },
521
- onPrompt: (prompt) => {
522
- return new Promise((resolve, reject) => {
523
- handleSubmit(prompt).then(() => {
524
- // The AI response will be displayed in chat.
525
- // We resolve with an empty string since the response is already shown.
526
- resolve('');
527
- }).catch(reject);
528
- });
529
- },
530
550
  onAgent: (task) => {
531
551
  return new Promise((resolve, reject) => {
532
552
  if (!projectContext) {
@@ -1369,7 +1389,9 @@ function handleCommand(command, args) {
1369
1389
  case 'deploy':
1370
1390
  case 'release':
1371
1391
  case 'publish': {
1372
- runSkill(command, args);
1392
+ runSkill(command, args).catch((err) => {
1393
+ app.notify(`Skill error: ${err.message}`);
1394
+ });
1373
1395
  break;
1374
1396
  }
1375
1397
  case 'skills': {
@@ -20,9 +20,10 @@ const BUILT_IN_SKILLS = [
20
20
  { name: 'message', description: 'Optional commit message (skips AI generation)', required: false },
21
21
  ],
22
22
  steps: [
23
- { type: 'prompt', content: 'Analyze the git diff and generate a conventional commit message following this format: type(scope): description. Types: feat, fix, docs, style, refactor, test, chore. Be concise.' },
23
+ { type: 'command', content: 'git diff --cached --stat || git diff --stat' },
24
+ { type: 'prompt', content: 'Based on this git diff, generate ONLY a conventional commit message (no explanation, no markdown). Format: type(scope): description. Types: feat, fix, docs, style, refactor, test, chore. Be concise. One line only.\n\n${_prev}' },
24
25
  { type: 'confirm', content: 'Commit with this message?' },
25
- { type: 'command', content: 'git add -A && git commit -m "${message}"' },
26
+ { type: 'command', content: 'git add -A && git commit -m "${_prev}"' },
26
27
  { type: 'notify', content: 'Changes committed successfully!' },
27
28
  ],
28
29
  },
@@ -707,6 +708,23 @@ export function parseSkillArgs(args, skill) {
707
708
  }
708
709
  return result;
709
710
  }
711
+ /**
712
+ * Sanitize text for safe use inside shell commands.
713
+ * Strips markdown formatting and escapes double quotes.
714
+ */
715
+ function sanitizeForShell(text) {
716
+ return text
717
+ // Strip markdown code blocks
718
+ .replace(/```[\s\S]*?```/g, '')
719
+ // Strip inline backticks
720
+ .replace(/`([^`]*)`/g, '$1')
721
+ // Strip bold/italic markers
722
+ .replace(/\*{1,3}([^*]+)\*{1,3}/g, '$1')
723
+ // Take only the first non-empty line (commit messages should be one line)
724
+ .split('\n').map(l => l.trim()).filter(Boolean)[0] || text.trim()
725
+ // Escape double quotes for shell safety
726
+ .replace(/"/g, '\\"');
727
+ }
710
728
  /**
711
729
  * Interpolate parameters into skill step content
712
730
  */
@@ -778,7 +796,9 @@ export async function executeSkill(skill, params, callbacks) {
778
796
  let lastOutput = '';
779
797
  for (const step of skill.steps) {
780
798
  // Interpolate params and ${_prev} into step content
781
- const allParams = { ...params, _prev: lastOutput };
799
+ // For command steps, sanitize _prev for safe shell usage
800
+ const sanitizedPrev = step.type === 'command' ? sanitizeForShell(lastOutput) : lastOutput;
801
+ const allParams = { ...params, _prev: sanitizedPrev };
782
802
  const content = interpolateParams(step.content, allParams);
783
803
  try {
784
804
  let result = '';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codeep",
3
- "version": "1.2.0",
3
+ "version": "1.2.2",
4
4
  "description": "AI-powered coding assistant built for the terminal. Multiple LLM providers, project-aware context, and a seamless development workflow.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",