* docs(flight-ticket-search): register skill in README table and add feature guide PR #224 머지 시 README "어떤 걸 할 수 있나" 표와 "포함된 기능" 리스트, 그리고 docs/features/flight-ticket-search.md 가이드가 등록되지 않아 main에 있는 다른 모든 스킬과 달리 사용자/에이전트가 README만 봐서는 이 스킬을 발견할 수 없는 상태였다. 누락분을 hotfix로 보강한다. - README 표에 `flight-ticket-search` 행 추가 (마이리얼트립 옆 항공 클러스터) - README "포함된 기능" 리스트에 가이드 링크 추가 - docs/features/flight-ticket-search.md 신규 작성: · 사용 시나리오, 구현 표면(fast-flights==2.2, 사용자 venv 격리) · search / compare-month / compare-range / compare-years CLI 예시 · 응답 필드, IATA 입력 가이드, 예약 링크 정책 · 검증된 노선 목록, 실패 모드, 비범위, 출처 검증: - node --test scripts/skill-docs.test.js → 138/138 pass - ./scripts/validate-skills.sh → skill layout looks valid 코드 변경 없음 → changeset 불필요. * feat(daiso-product-search): replace blocked-API fallback with Bearer token auth selStrPkupStck는 더 이상 차단 상태가 아니며, /api/auth/request로 비로그인 JWT를 발급받아 AES-128-CBC(키: PRE_AUTH_ENC_KEY)로 암호화한 Bearer 토큰으로 접근한다. 403 응답 시 토큰을 재발급해 1회 재시도한다. pickupEligibility(selPkupStr) 폴백 로직은 제거했다. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Preserve Daiso pickup answers when Bearer auth degrades Keep exact stock lookup on the official Bearer-token path while restoring the public selPkupStr fallback for repeated auth blocks. Constraint: PR #250 review required Bearer auth to remain primary without removing the resilient pickup eligibility API. Rejected: Throwing after the retry | it collapses callers back to a brittle single upstream-auth dependency. Confidence: high Scope-risk: narrow Directive: Keep pickupStock quantity semantics separate from pickupEligibility yes/no fallback. Tested: node --test packages/daiso-product-search/test/index.test.js; npm test --workspace daiso-product-search; npm run lint --workspace daiso-product-search; npm run ci; live lookupStoreProductAvailability smoke for 강남역2호점 / VT 리들샷 100. Not-tested: Live forced 403 from Daiso upstream; covered with injected fetch regression tests. * Prove Daiso stock retry sends auth headers Strengthen the retry regression so the Bearer-token contract cannot regress while still returning success from mocked stock responses.\n\nConstraint: PR #250 review requested explicit Authorization, X-DM-UID, and request body assertions on the retry path.\nRejected: Counting requests only | it allowed header/body regressions to pass.\nConfidence: high\nScope-risk: narrow\nDirective: Keep auth-header assertions on both initial and retry stock requests when editing this flow.\nTested: node --test packages/daiso-product-search/test/index.test.js; npm test --workspace daiso-product-search; npm run lint --workspace daiso-product-search; npm run ci; live lookupStoreProductAvailability smoke for 강남역2호점 / VT 리들샷 100; repeated-403 fixture probe.\nNot-tested: Live repeated upstream 403 because forcing Daiso production auth failure is not available without changing upstream state. * Preserve Daiso caller headers through Bearer stock lookup Keep advanced caller headers on the authenticated stock endpoint while generated Bearer and X-DM-UID values remain authoritative. Document the degraded selPkupStr fallback order in skill and source docs so the public workflow matches the restored API surface.\n\nConstraint: PR #250 review required resilient Bearer-primary stock lookup plus selPkupStr fallback and header/body contract coverage.\nRejected: Replacing caller headers with only auth headers | It regressed tracing/test-control header pass-through.\nConfidence: high\nScope-risk: narrow\nDirective: Keep Authorization and X-DM-UID generated by the auth flow even when callers provide same-named headers.\nTested: node --test packages/daiso-product-search/test/index.test.js; npm test --workspace daiso-product-search; npm run lint --workspace daiso-product-search; node --test scripts/skill-docs.test.js; npm run ci; live lookupStoreProductAvailability smoke for 강남역2호점 / VT 리들샷 100.\nNot-tested: Forced live upstream repeated 403; covered by injected fixture tests. * fix(danawa-price-search): capture .ico.* payment-condition badges and surface as row labels PR #226 row 파서에 결제조건 배지(`.ico.cash`/`.ico.point`/`.ico.coupon`/`.ico.card`) selector가 누락돼, 카드 결제 불가능한 현금/쿠폰/포인트 전용가가 일반 최저가로 노출되는 결함을 고친다. - `offers()` row 파싱부에 결제조건 배지 화이트리스트 캡처 블록 추가 (클래스 `cash`/`point`/`coupon`/`discount`/`card`/`membership` 또는 텍스트 `현금`/`포인트`/`쿠폰`/`할인`만 인정 — 빠른배송/안내/상품리뷰 노이즈 차단) - row dict 신규 필드 6개: `payment_badges`, `cash_only`, `point_only`, `coupon_only`, `card_only_badge`, `is_conditional_price` - 반환 dict에 `normal_count`, `conditional_count` 추가 - `SKILL.md` / `docs/features/danawa-price-search.md` 갱신 (Output shape · Response style · Workflow · Failure modes에 결제조건 정책과 표 예시 명시) 정렬 정책은 그대로 `total_price` 단일 기준이며, 결제조건은 row 단위 플래그/라벨로만 노출해 호출자가 결제수단에 맞춰 직접 판단하도록 한다. 회귀 (pcode=75001853, 갤럭시 S25 256GB 자급제 `offers --limit 5`): - 1위 킴스클럽 979,000원 / `cash_only=True` / `payment_badges=["현금"]` - 2위 롯데ON 1,072,080원 / `cash_only=False` / `payment_badges=[]` - 3~5위 일반가 row 모두 `payment_badges` 빈 리스트 (노이즈 0건) Closes #252 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Ensure captured Danawa payment badges stay conditional Classify every whitelisted payment badge into normalized condition types so callers cannot count captured discount, membership, or text-only card rows as normal prices. Constraint: PR #253 review required TDD follow-up on feature/#252 without changing total_price sorting.\nRejected: Removing discount and membership from the whitelist | would lose Danawa condition labels already captured by the parser.\nConfidence: high\nScope-risk: narrow\nDirective: Keep payment_badge whitelist and payment_condition_types in sync whenever adding new badge classes or text keywords.\nTested: PYTHONPATH=.:scripts python3 -m unittest scripts.test_danawa_price_search; live offers 75001853 --limit 5; npm run lint; npm run typecheck; npm run test; architect verification CLEAR.\nNot-tested: Danawa markup variants not represented by current live page or synthetic badge fixtures. * Keep icon-only Danawa payment badges visible Class-only Danawa payment icons can carry eligibility information without visible text, so synthesize display labels from the same normalized condition map used for types and booleans. This keeps raw row labels, condition fields, and returned-window counts aligned for downstream table renderers.\n\nConstraint: PR #253 review follow-up requires TDD coverage before parser changes.\nRejected: Leaving payment_badges text-only | icon-only conditional rows would still render without visible payment labels.\nConfidence: high\nScope-risk: narrow\nDirective: Derive future payment badge labels, types, and booleans from one canonical mapping.\nTested: python3 -m py_compile danawa-price-search/scripts/danawa_search.py scripts/test_danawa_price_search.py; PYTHONPATH=.:scripts python3 -m unittest scripts.test_danawa_price_search; python3 danawa-price-search/scripts/danawa_search.py offers 75001853 --limit 5; npm run lint; npm run typecheck; npm run test\nNot-tested: Danawa icon-only markup was verified with synthetic fixtures rather than a live page snapshot. * Merge pull request #249 from NomaDamas/feature/#248 Feature/#248 * Restore SH notice lookup without proxy policy drift Reintroduce SH notice search as a direct public HTML client so the skill complies with the free-API proxy boundary while preserving verifiable keyword, pagination, and attachment behavior. Constraint: i-sh.co.kr board is public unauthenticated HTML, so k-skill-proxy must not host the scraper.\nRejected: Re-adding /v1/sh-notice proxy routes | public HTML scraping in proxy violates repository policy.\nConfidence: high\nScope-risk: moderate\nDirective: Keep SH public HTML access local/direct unless a key-required official free API is discovered and documented.\nTested: npm run ci; npm run lint --workspace sh-notice-search; npm test --workspace sh-notice-search; live SH smoke for 행복주택, 매입임대, 신혼희망타운, page 1/page 5, 1/6/9/11/0 attachment details.\nNot-tested: authenticated SH flows, 청약 application/submission, direct attachment downloads. * Preserve public SH helper semantics Route exported URL builders through the same normalization as the CLI/API so natural category aliases cannot bypass srchTp title narrowing or category mapping.\n\nConstraint: PR #254 review found exported helper callers could pass Korean/English public category inputs and get broken or broadened SH URLs.\nRejected: Keep normalized-only fast paths | exported helpers are public API and must protect natural inputs.\nConfidence: high\nScope-risk: narrow\nDirective: Keep exported helper behavior aligned with normalizeSearchOptions and normalizeDetailOptions when adding new public aliases.\nTested: npm test --workspace sh-notice-search; npm run lint --workspace sh-notice-search; npm run typecheck; npm run ci; node helper smoke for 임대 search/detail URLs.\nNot-tested: Live SH network smoke was not rerun for this helper-only change. * Preserve SH parser helper aliases Route exported parser helpers through the same public normalizers used by the SH fetch and URL-builder APIs so natural category aliases stay consistent across the package surface. Constraint: PR #254 Round 2 review found parser helpers still treated raw category aliases as pre-normalized inputs. Rejected: Keep parser helpers normalized-only | inconsistent with exported URL builders and public helper ergonomics. Confidence: high Scope-risk: narrow Directive: Keep exported SH helper entry points on canonical normalizeSearchOptions/normalizeDetailOptions unless a separate internal-only API is introduced. Tested: npm test --workspace sh-notice-search; npm run lint --workspace sh-notice-search; npm run typecheck; npm pack --workspace sh-notice-search --dry-run; npm run ci; parser smoke for Korean 임대 list/detail helpers; Ralph architect verification CLEAR; post-deslop regression npm run ci Not-tested: Live SH network smoke for this follow-up; fixture and injected-fetch coverage exercised the helper contract. * Make SH parser failures explicit Warn when SH returns block or maintenance HTML without the expected public board markup, and constrain exposed preview links to the SH converter origin/path.\n\nConstraint: Round 3 review required TDD coverage for block/maintenance HTML and untrusted preview URLs.\nRejected: Throwing on unexpected HTML | Existing parser helpers return partial fixture-friendly results, so warnings preserve compatibility while exposing failure evidence.\nConfidence: high\nScope-risk: narrow\nDirective: Keep SH public HTML lookup direct; do not add proxy routing unless a key-required official free API is adopted.\nTested: npm run lint --workspace sh-notice-search; npm test --workspace sh-notice-search; npm run typecheck; npm pack --workspace sh-notice-search --dry-run; npm run ci; Node smoke for blocked HTML warnings and external preview filtering.\nNot-tested: Live blocked/NetFunnel SH response, because no live blocked page was available during implementation. * ci: install beautifulsoup4 so danawa price search tests can import bs4 The new scripts/test_danawa_price_search.py imports danawa_search.py, which requires beautifulsoup4. CI only runs npm ci, so the bs4 import fails with 'beautifulsoup4 is required: python -m pip install beautifulsoup4' and the validate job exits with code 1. Install beautifulsoup4 via pip before running npm run ci so the Python test suite can import danawa_search and run the new payment badge regression tests. * Revert "ci: install beautifulsoup4 so danawa price search tests can import bs4" This reverts commit |
||
|---|---|---|
| .. | ||
| bin | ||
| config | ||
| launchd | ||
| test | ||
| .gitignore | ||
| AGENTS.md | ||
| install.sh | ||
| Makefile | ||
| README.md | ||
| uninstall.sh | ||
k-skill-qa-bot
Automated QA daemon for the k-skill skill library. Runs every 3 days via macOS launchd, tests every suitable skill via codex exec --json --dangerously-bypass-approvals-and-sandbox, has a read-only/no-approval LLM judge grade pass/fail/skip, and files dedup'd GitHub issues for skills that have broken.
What it does
- Refreshes a shallow clone of
NomaDamas/k-skillmainevery 3 days. - Discovers every
<skill>/SKILL.md. - Classifies each skill (read-only / location / login / destructive / api-key / proxy-dependent / deprecated).
- Runs each suitable skill through
codex exec --json --dangerously-bypass-approvals-and-sandboxwith a smoke-test prompt synthesized from the skill's## When to usebullets. The daemon runs as a dedicated LaunchAgent with non-interactive approvals; avoiding the Codex sandbox prevents false DNS/network failures during skill smoke tests. - Judges the result via a second read-only/no-approval
codex execcall using the configured judge model and a strict JSON Schema. - Files dedup'd issues on
NomaDamas/k-skillfor true failures (withauto-qalabel). Skipped skills (deprecated, login-required, missing API key) never create issues.
The k-skill repo itself is never modified by the bot — it is read-only SSOT. Test prompts are synthesized from each SKILL.md.
Install
Prereqs (one-time):
brew install bats-core coreutils gh jq python@3
pip3 install pyyaml jsonschema pytest
codex --version # codex-cli >= 0.130
codex login # one-time
gh auth login # one-time, needs `repo` scope
Then:
cd /path/to/k-skill
bash tools/k-skill-qa-bot/install.sh
Re-run install.sh to upgrade — it is idempotent and preserves state/.
Configure
The default CREATE_ISSUES=false means the first run does NOT file any issues. After reviewing the first summary.md, opt in:
echo 'CREATE_ISSUES=true' >> ~/.local/share/k-skill-qa-bot/.env
Overridable variables (see config/defaults.sh):
| Var | Default | Meaning |
|---|---|---|
CREATE_ISSUES |
false |
File GH issues for failures |
CODEX_MODEL |
gpt-5.5 |
Model for skill exec |
JUDGE_MODEL |
gpt-5.5 |
Model for LLM judge |
CODEX_PROVIDER |
openai |
Codex model provider for skill exec and judge calls |
TIMEOUT_SECS |
180 |
Per-skill timeout |
JUDGE_TIMEOUT_SECS |
60 |
Per-judge timeout |
MAX_PARALLEL |
4 |
Concurrent skill tests |
LAST_RUN_MIN_AGE |
259200 |
Min seconds between runs (72h) |
GH_REPO |
NomaDamas/k-skill |
Where to file issues |
config/skill-overrides.yml controls per-skill force_skip and category overrides. Destructive booking flows (ktx-booking, srt-booking, catchtable-sniper, etc.) and session-required skills (kakaotalk-mac, hipass-receipt, toss-securities, iros-registry-automation) are force-skipped by default so the bot never abuses an account.
Logs and inspection
tail -f ~/Library/Logs/k-skill-qa-bot/stderr.log
cat ~/.local/share/k-skill-qa-bot/state/runs/$(ls -t ~/.local/share/k-skill-qa-bot/state/runs/ | head -1)/summary.md
The bot keeps the most recent 12 runs and purges older ones.
Force a run
~/.local/share/k-skill-qa-bot/bin/run-qa.sh --force
~/.local/share/k-skill-qa-bot/bin/run-qa.sh --force --only kbo-results
~/.local/share/k-skill-qa-bot/bin/run-qa.sh --force --dry-run # no issues regardless of CREATE_ISSUES
Uninstall
bash ~/.local/share/k-skill-qa-bot/uninstall.sh
bash ~/.local/share/k-skill-qa-bot/uninstall.sh --yes --purge --purge-logs
Safety
- Skill smoke tests use
--dangerously-bypass-approvals-and-sandboxbecause the Codex sandbox can block legitimate DNS/network lookups for public skill endpoints exercised by smoke tests. - A dedicated LaunchAgent is scheduling isolation only; it is not a separate OS user, container, or filesystem sandbox.
- The bot-managed clone is not write-protected from the unsandboxed smoke agent; treat it as mutable bot state and judge only against inputs whose provenance is understood.
- The LLM judge stays on the safer
-s read-onlypath withapproval_policy="never"; read-only/no-approval limits writes and approval prompts, but does not make the judge a no-tools or file-isolated model call. Treat transcript and skill Markdown as untrusted input. - 10 destructive/login-required skills are force-skipped before any codex call is issued.
- Deprecated skills (
~~name~~ ⚠️ 지원 중단in README) are detected and skipped. update-clone.shrefuses anyK_SKILL_CLONEoutsideK_QA_HOME/k-skill-cloneunlessALLOW_EXTERNAL_CLONE_TARGET=1(prevents the script from git-reset'ing the wrong directory).CREATE_ISSUES=falsefirst-run default prevents accidental issue spam.- Local state only:
~/.local/share/k-skill-qa-bot/. Expected network egress is limited to git fetch, codex API, gh API, k-skill-proxy health checks, and the public skill endpoints exercised by smoke tests.
Troubleshooting
codex: command not found→ check the plist'sEnvironmentVariables.PATH. Default is/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin.gh: not authenticated→ rungh auth loginwithreposcope.gtimeout: command not found→brew install coreutils.- LaunchAgent state via
launchctl print "gui/$(id -u)/org.nomadamas.k-skill-qa-bot" | head. - Force a re-run:
launchctl kickstart -k "gui/$(id -u)/org.nomadamas.k-skill-qa-bot".