Skip to content
Back to skills

Star Prompt

ASecurity

CLI 프로젝트의 setup/postinstall 흐름에 GitHub 스타 요청을 추가한다. 'star prompt', '스타 요청', '리포 스타', 'star request', '깃헙 스타 넣어줘', 'star 눌러달라고', '응원 요청' 같은 요청에 사용한다.

  • 7 stars
  • 0 votes
  • 0 copies
  • 2 views
  • Added September 22, 2026
toolsjavascriptjavanodegitapi

Works with

  • cli
  • api

Security analysis

A100/100

Pro scans all 2 files and shows the line behind each finding

Scanned September 22, 2026

npx -y skills add tellang/triflux --skill star-prompt --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Star Prompt?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Star Prompt
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/tellang-star-prompt/badge)](https://www.skillsdirectory.com/skills/tellang-star-prompt)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: star-prompt
description: >-
  CLI 프로젝트의 setup/postinstall 흐름에 GitHub 스타 요청을 추가한다.
  'star prompt', '스타 요청', '리포 스타', 'star request', '깃헙 스타 넣어줘',
  'star 눌러달라고', '응원 요청' 같은 요청에 사용한다.
---

# tfx-star-prompt — GitHub Star Request Prompt

CLI 도구의 setup/postinstall 완료 시점에 GitHub 리포 스타 요청을 추가한다.
기본 모드는 aggressive(모달 차단형)이며, `--soft`를 전달하면 기존 부드러운 confirm 모드로 폴백한다.
CI/비인터랙티브 환경에서는 자동으로 soft 모드로 강등한다.

## 동작 흐름

```
detectInteractive() ─── false → soft 모드 강제
       │
       ✓ true
       │
gh --version ─── 실패 → URL만 표시
       │
       ✓ 설치됨
       │
gh auth status ─── 실패 → URL만 표시
       │
       ✓ 인증됨
       │
gh api user/starred/{owner}/{repo}
       ├─ 성공 → "이미 함께하고 계시군요. ⭐" + markPrompted()
       ├─ 404  → 미스타로 진행
       └─ 그 외 에러 → 프롬프트 없이 URL만 표시 (마커 남기지 않음)
       │
       ✗ 미스타
       │
이미 프롬프트 본 유저(마커 존재)면 즉시 스킵
       │
aggressive 기본: AskUserQuestion([예, 누를게요] / [아니오]) 블로킹 선택
soft(--soft): confirm("⭐ 하나가 큰 차이를 만듭니다.")
       │
       ├─ 아니오 → aggressive: 안내 + URL / soft: URL만 + markPrompted()
       └─ 예
       │
       Y
       │
gh api -X PUT /user/starred/{owner}/{repo}
       ├─ 성공 → aggressive: "감사합니다! 여러분의 ⭐가 프로젝트를 성장시킵니다."
       │         soft: "함께해 주셔서 감사합니다. ⭐"
       └─ 실패 → URL 폴백
       │
모든 프롬프트 완료 경로는 markPrompted() 호출
```

## 구현 패턴

### 유틸리티 계약

- `ok(message)`: 성공 메시지 출력(초록/강조 톤)
- `info(message)`: 일반 안내 메시지 출력
- `warn(message)`: 경고/실패 폴백 메시지 출력
- `confirm(message, defaultValue)`: soft 모드용 Y/n 확인
- `askUserQuestion({ question, options })`: aggressive 모달 선택 UI
  - 옵션은 정확히 `[예, 누를게요]`, `[아니오]`
  - 선택 전까지 흐름을 블로킹한다

### 전체 `starRequest` 교체 패턴

아래 패턴으로 기존 `starRequest` 함수를 전면 교체한다.

```javascript
import fs from "node:fs";
import os from "node:os";
import path from "node:path";
import { execFileSync } from "node:child_process";

function detectInteractive() {
  if (!process.stdout.isTTY) return false;
  if (process.env.CI) return false;
  if (process.env.TERM === "dumb") return false;
  return true;
}

function runGh(args) {
  return execFileSync("gh", args, {
    timeout: 10000,
    encoding: "utf8",
    stdio: ["pipe", "pipe", "pipe"],
  });
}

function getHttpStatus(error) {
  const out = [error?.stdout, error?.stderr].filter(Boolean).join("\n");
  const match = out.match(/HTTP\s+(\d{3})/i) || out.match(/\b(\d{3})\b/);
  return match ? Number(match[1]) : null;
}

export async function starRequest({
  owner,
  repo,
  soft = false,
  askUserQuestion,
  confirm,
  ok,
  info,
  warn,
}) {
  const repoUrl = `https://github.com/${owner}/${repo}`;
  const interactive = detectInteractive();
  const useSoft = soft || !interactive;

  const MARKER_DIR = path.join(os.homedir(), ".config", "star-prompt");
  const MARKER = path.join(MARKER_DIR, `${owner}-${repo}.prompted`);

  const markPrompted = () => {
    fs.mkdirSync(MARKER_DIR, { recursive: true });
    fs.writeFileSync(MARKER, new Date().toISOString(), "utf8");
  };

  if (fs.existsSync(MARKER)) return;

  try {
    runGh(["--version"]);
  } catch {
    info(repoUrl);
    return;
  }

  try {
    runGh(["auth", "status"]);
  } catch {
    info(repoUrl);
    return;
  }

  let alreadyStarred = false;
  try {
    runGh(["api", `user/starred/${owner}/${repo}`]);
    alreadyStarred = true;
  } catch (error) {
    const status = getHttpStatus(error);
    if (status === 404) {
      alreadyStarred = false;
    } else {
      // API 에러(404 외): 프롬프트 없이 URL만 출력, 마커 미기록
      warn(repoUrl);
      return;
    }
  }

  if (alreadyStarred) {
    ok("이미 함께하고 계시군요. ⭐");
    markPrompted();
    return;
  }

  let accepted = false;
  if (useSoft) {
    accepted = await confirm("⭐ 하나가 큰 차이를 만듭니다.", true);
  } else {
    const answer = await askUserQuestion({
      question: "⭐ 이 프로젝트가 마음에 드셨나요? 스타를 누르시겠습니까?",
      options: ["예, 누를게요", "아니오"],
    });
    accepted = answer === "예, 누를게요";
  }

  if (!accepted) {
    if (useSoft) {
      info(repoUrl);
    } else {
      info(`괜찮습니다. 나중에 마음이 바뀌시면: ${repoUrl}`);
    }
    markPrompted();
    return;
  }

  try {
    runGh(["api", "-X", "PUT", `/user/starred/${owner}/${repo}`]);
    if (useSoft) {
      ok("함께해 주셔서 감사합니다. ⭐");
    } else {
      ok("감사합니다! 여러분의 ⭐가 프로젝트를 성장시킵니다.");
    }
  } catch {
    warn(repoUrl);
  } finally {
    markPrompted();
  }
}
```

## 적용 시 규칙

1. 기본 모드는 aggressive이며 `--soft` 전달 시에만 soft 모드로 전환한다.
2. CI/비인터랙티브 감지는 아래 조건으로 강제 soft 폴백한다.
   - `!process.stdout.isTTY || process.env.CI || process.env.TERM === "dumb"`
3. gh 설치 확인은 `which` 대신 `gh --version`으로 수행한다 (크로스플랫폼).
4. gh 인증 확인은 `gh auth status`로 수행한다.
5. 스타 여부 체크는 `gh api user/starred/{owner}/{repo}`를 사용하고, `404`만 미스타로 판단한다.
6. 스타 체크 API 에러가 `404` 외 상태면 프롬프트를 띄우지 않고 URL만 출력하며 마커를 남기지 않는다.
7. 중복 요청 방지를 위해 아래 마커를 사용한다.
   - `~/.config/star-prompt/{owner}-{repo}.prompted`
   - 이미 스타한 유저도 `markPrompted()`를 호출한다.
8. 모든 gh 호출은 `timeout: 10000`, `stdio: ["pipe","pipe","pipe"]`를 사용한다.
9. setup/postinstall의 핵심 성공/실패 처리 흐름을 막지 않도록, 스타 로직 실패는 모두 조용히 폴백한다.

## UX 카피 테이블

| 상황 | Aggressive (기본) | Soft (`--soft`) |
|------|-------------------|-----------------|
| 이미 스타 | `이미 함께하고 계시군요. ⭐` | (동일) |
| 요청 | `AskUserQuestion: ⭐ 이 프로젝트가 마음에 드셨나요? 스타를 누르시겠습니까?` | `⭐ 하나가 큰 차이를 만듭니다.` |
| 수락 후 | `감사합니다! 여러분의 ⭐가 프로젝트를 성장시킵니다.` | `함께해 주셔서 감사합니다. ⭐` |
| 거절 | `괜찮습니다. 나중에 마음이 바뀌시면:` + URL | URL만 |
| gh 미설치 | URL만 표시 | (동일) |
| gh 미인증 | URL만 표시 | (동일) |
| CI/비인터랙티브 | soft 자동 폴백 | (동일) |
| API 에러 (404 외) | 프롬프트 없이 URL만 표시, 마커 안 남김 | (동일) |
| 이미 프롬프트 본 유저 | 스킵 | (동일) |

Files in this skill

  • SKILL.md7.2 KB
  • skill.json297 B

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.