@cliphijack/santaclaude 1.0.17 → 1.0.19

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/santaclaude.js +6 -8
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cliphijack/santaclaude",
3
- "version": "1.0.17",
3
+ "version": "1.0.19",
4
4
  "publishConfig": { "access": "public" },
5
5
  "description": "SantaClaude 커넥터 — 클라우드 예약을 내 로컬 Claude(tmux)에 발사",
6
6
  "bin": { "santaclaude": "./santaclaude.js" },
package/santaclaude.js CHANGED
@@ -308,7 +308,7 @@ description: 산타 Gbrain — 내 노트·문서·지식 폴더에서 답을
308
308
  }
309
309
  };
310
310
  // 확장자 → MIME (보낼 파일 타입 판별)
311
- const EXT_MIME = { png: 'image/png', jpg: 'image/jpeg', jpeg: 'image/jpeg', gif: 'image/gif', webp: 'image/webp', svg: 'image/svg+xml', bmp: 'image/bmp', pdf: 'application/pdf', txt: 'text/plain', md: 'text/markdown', csv: 'text/csv', json: 'application/json', log: 'text/plain', html: 'text/html', xml: 'text/xml', mp3: 'audio/mpeg', wav: 'audio/wav', ogg: 'audio/ogg', m4a: 'audio/mp4', flac: 'audio/flac', mp4: 'video/mp4', webm: 'video/webm', mov: 'video/quicktime', mkv: 'video/x-matroska', zip: 'application/zip', gz: 'application/gzip', tar: 'application/x-tar' };
311
+ const EXT_MIME = { png: 'image/png', jpg: 'image/jpeg', jpeg: 'image/jpeg', gif: 'image/gif', webp: 'image/webp', svg: 'image/svg+xml', bmp: 'image/bmp', pdf: 'application/pdf', txt: 'text/plain', md: 'text/markdown', csv: 'text/csv', json: 'application/json', log: 'text/plain', html: 'text/html', xml: 'text/xml', mp3: 'audio/mpeg', wav: 'audio/wav', ogg: 'audio/ogg', m4a: 'audio/mp4', flac: 'audio/flac', mp4: 'video/mp4', webm: 'video/webm', mov: 'video/quicktime', mkv: 'video/x-matroska', zip: 'application/zip', gz: 'application/gzip', tar: 'application/x-tar', xlsx: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', xls: 'application/vnd.ms-excel', docx: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', doc: 'application/msword', pptx: 'application/vnd.openxmlformats-officedocument.presentationml.presentation', ppt: 'application/vnd.ms-powerpoint', rtf: 'application/rtf', yaml: 'text/yaml', yml: 'text/yaml' };
312
312
  function installDefaultSkills(cwd) {
313
313
  const base = cwd && fs.existsSync(cwd) ? cwd : os.homedir();
314
314
  for (const [name, files] of Object.entries(DEFAULT_SKILLS)) {
@@ -370,13 +370,11 @@ async function run(conf) {
370
370
  let r; try { r = await fetch(api + '/api/skill/get?token=' + encodeURIComponent(token) + '&id=' + encodeURIComponent(cmd.skillId || '')); } catch (e) { console.warn(' 스킬 받기 실패'); return; }
371
371
  if (!r.ok) { console.warn(' 스킬 없음'); return; }
372
372
  const sk = await r.json();
373
- // 창의 작업 폴더 (없으면 )
374
- let cwd = os.homedir();
375
- try { const pc = execFileSync('tmux', ['display-message', '-t', w, '-p', '#{pane_current_path}'], { encoding: 'utf8' }).trim(); if (pc) cwd = pc; } catch (e) {}
373
+ // 전역 스킬 박스 받은 스킬은 ~/.claude/skills/ 에 둬서 모든 루돌프·폴더에서 바로 쓰게 ( 확정: 전역 기본)
376
374
  // 폴더 네임스페이스 — santa-{퍼블리셔}-{스킬}: 충돌 방지 + 마켓 출처 식별. SKILL.md 파일명은 표준 고정
377
375
  const pub = String(sk.publisher || 'me').replace(/[^a-zA-Z0-9-]/g, '').toLowerCase() || 'me';
378
376
  const folder = ('santa-' + pub + '-' + sk.name).replace(/[^a-zA-Z0-9._-]/g, '-').slice(0, 80);
379
- const sdir = path.join(cwd, '.claude', 'skills', folder);
377
+ const sdir = path.join(os.homedir(), '.claude', 'skills', folder);
380
378
  let cnt = 0;
381
379
  for (const f of (sk.files || [])) {
382
380
  const safe = String(f.path || '').replace(/\\/g, '/').replace(/(^|\/)\.\.(\/|$)/g, '/').replace(/^\/+/, '');
@@ -385,8 +383,8 @@ async function run(conf) {
385
383
  if (!fp.startsWith(sdir)) continue; // 경로 탈출 방지
386
384
  try { fs.mkdirSync(path.dirname(fp), { recursive: true }); fs.writeFileSync(fp, String(f.content || '')); cnt++; } catch (e) {}
387
385
  }
388
- console.log(` 🧩 스킬 "${sk.name}" ${cnt}개 파일 이식 → ${sdir}`);
389
- const msg = (sk.prompt && sk.prompt.trim() ? sk.prompt.trim() + ' ' : '') + '(스킬 "' + sk.name + '"이 .claude/skills/' + folder + '/ 에 이식됨 — SKILL.md 읽고 적용)';
386
+ console.log(` 🧩 스킬 "${sk.name}" ${cnt}개 파일 → ${sdir} (전역)`);
387
+ const msg = (sk.prompt && sk.prompt.trim() ? sk.prompt.trim() + ' ' : '') + '(스킬 "' + sk.name + '"이 ~/.claude/skills/' + folder + '/ 에 이식됨 — 전역이라 모든 세션에서 사용 가능. SKILL.md 읽고 적용)';
390
388
  execFileSync('tmux', ['send-keys', '-t', w, '-l', msg]);
391
389
  setTimeout(() => { try { execFileSync('tmux', ['send-keys', '-t', w, 'Enter']); } catch (e) {} }, 300);
392
390
  return;
@@ -404,7 +402,7 @@ async function run(conf) {
404
402
  const j = await r.json();
405
403
  const mime = String(j.mime || '');
406
404
  if (!mime.startsWith('image/')) allImg = false;
407
- const safe = String(im.name || j.name || '').replace(/[^A-Za-z0-9._-]/g, '_').slice(0, 80);
405
+ const safe = String(im.name || j.name || '').replace(/[\/\\\x00-\x1f]/g, '').replace(/\s+/g, '_').slice(0, 80); // 한글 등 유니코드 보존, 경로·제어문자만 제거(execFileSync 무쉘이라 안전)
408
406
  let fname;
409
407
  if (safe && /\.[A-Za-z0-9]+$/.test(safe)) fname = Date.now() + '_' + paths.length + '_' + safe; // 원본 파일명·확장자 보존
410
408
  else { const ext = MIME_EXT[mime] || (mime.includes('png') ? 'png' : mime.includes('webp') ? 'webp' : mime.startsWith('image/') ? 'jpg' : 'bin'); fname = Date.now() + '_' + paths.length + '.' + ext; }