@haposoft/cafekit 0.8.10 → 0.8.12
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.
- package/README.md +2 -1
- package/bin/install.js +4 -3
- package/package.json +1 -1
- package/src/claude/gitignore +3 -1
- package/src/claude/hooks/lib/config.cjs +1 -0
- package/src/claude/hooks/lib/skill-router-routes.cjs +190 -0
- package/src/claude/hooks/skill-router.cjs +68 -0
- package/src/claude/migration-manifest.json +4 -1
- package/src/claude/scripts/validate-spec-output.cjs +35 -5
- package/src/claude/settings/settings.json +4 -0
- package/src/claude/skills/debug/SKILL.md +56 -23
- package/src/claude/skills/develop/SKILL.md +78 -6
- package/src/claude/skills/develop/references/implementation-notes-template.html +320 -0
- package/src/claude/skills/develop/references/quality-gate.md +25 -0
- package/src/claude/skills/hotfix/SKILL.md +69 -18
- package/src/claude/skills/hotfix/references/prevention-gate.md +1 -1
- package/src/claude/skills/hotfix/references/review-cycle.md +9 -9
- package/src/claude/skills/hotfix/references/workflow-specialized.md +2 -2
- package/src/claude/skills/specs/SKILL.md +11 -7
- package/src/claude/skills/specs/references/review.md +3 -0
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
> Claude Code-first spec-driven workflow and runtime bundle for AI coding assistants.
|
|
4
4
|
|
|
5
|
-
[](https://github.com/haposoft/cafekit)
|
|
6
6
|
[](LICENSE)
|
|
7
7
|
[](https://claude.ai/code)
|
|
8
8
|
|
|
@@ -46,6 +46,7 @@ Claude Code install targets:
|
|
|
46
46
|
|
|
47
47
|
```text
|
|
48
48
|
.claude/
|
|
49
|
+
├── .gitignore
|
|
49
50
|
├── skills/
|
|
50
51
|
├── agents/
|
|
51
52
|
├── hooks/
|
package/bin/install.js
CHANGED
|
@@ -751,7 +751,8 @@ function copyClaudeRuntimeFiles(platformKey, results, options = {}) {
|
|
|
751
751
|
|
|
752
752
|
manifest.runtime.files.forEach(relPath => {
|
|
753
753
|
const srcPath = path.join(srcBase, relPath);
|
|
754
|
-
const
|
|
754
|
+
const targetRelPath = relPath === 'gitignore' ? '.gitignore' : relPath;
|
|
755
|
+
const targetPath = path.join(targetBase, targetRelPath);
|
|
755
756
|
|
|
756
757
|
if (!fs.existsSync(srcPath)) {
|
|
757
758
|
console.log(` ⚠ Runtime file not found: ${relPath}`);
|
|
@@ -767,10 +768,10 @@ function copyClaudeRuntimeFiles(platformKey, results, options = {}) {
|
|
|
767
768
|
fs.copyFileSync(srcPath, targetPath);
|
|
768
769
|
|
|
769
770
|
if (targetExists) {
|
|
770
|
-
console.log(` ↻ Runtime updated: ${
|
|
771
|
+
console.log(` ↻ Runtime updated: ${targetRelPath}`);
|
|
771
772
|
results.updated++;
|
|
772
773
|
} else {
|
|
773
|
-
console.log(` ✓ Runtime installed: ${
|
|
774
|
+
console.log(` ✓ Runtime installed: ${targetRelPath}`);
|
|
774
775
|
results.copied++;
|
|
775
776
|
}
|
|
776
777
|
} else {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@haposoft/cafekit",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.12",
|
|
4
4
|
"description": "Claude Code-first spec-driven workflow for AI coding assistants. Bundles CafeKit hapo: skills, runtime hooks, agents, and installer scaffolding.",
|
|
5
5
|
"author": "Haposoft <nghialt@haposoft.com>",
|
|
6
6
|
"license": "MIT",
|
package/src/claude/gitignore
CHANGED
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
const MIN_SCORE = 4;
|
|
2
|
+
const WEIGHTS = { strong: 6, medium: 3, weak: 1, negative: -5 };
|
|
3
|
+
|
|
4
|
+
const ROUTES = [
|
|
5
|
+
route('hapo:hotfix', 'urgent fix or production regression', 100, {
|
|
6
|
+
strong: ['hotfix', 'fix bug', 'fix lỗi', 'fix loi', 'sửa lỗi', 'sua loi', 'production bug', 'prod bug', 'regression', 'lỗi production', 'loi production', 'sửa gấp', 'sua gap', 'バグ修正', '不具合修正', '本番障害', '本番バグ', '緊急修正', '至急修正', 'リグレッション'],
|
|
7
|
+
medium: ['khẩn cấp', 'khan cap', 'production', 'critical bug', 'rollback', '修正して', '直して', '緊急', '本番', '重大バグ', 'ロールバック', '障害対応'],
|
|
8
|
+
weak: ['fix', 'fixing', 'sửa', 'sua', 'urgent', 'emergency', 'release is broken', '修正', '直す', '至急', 'リリースが壊れた'],
|
|
9
|
+
negative: ['slide', 'pptx', 'spec', 'brainstorm'],
|
|
10
|
+
}),
|
|
11
|
+
route('hapo:debug', 'bug investigation or failure diagnosis', 90, {
|
|
12
|
+
strong: ['debug', 'root cause', 'diagnose', 'stack trace', 'không chạy', 'khong chay', 'デバッグ', '原因調査', '根本原因', 'スタックトレース', '動かない'],
|
|
13
|
+
medium: ['bug', 'error', 'exception', 'failing', 'failed', 'failure', 'broken', 'lỗi', 'loi', 'tại sao', 'tai sao', 'vì sao', 'vi sao', 'nguyên nhân', 'nguyen nhan', 'ci fail', 'build fail', 'バグ', 'エラー', '例外', '失敗', '壊れている', 'なぜ', '原因', 'ci失敗', 'ビルド失敗'],
|
|
14
|
+
weak: ['sai', 'fail', 'issue', 'problem', '問題', '不具合'],
|
|
15
|
+
negative: ['commit', 'push', 'slide', 'pptx'],
|
|
16
|
+
}),
|
|
17
|
+
route('hapo:specs', 'specification, requirements, design, tasks, or spec validation', 80, {
|
|
18
|
+
strong: ['spec', 'specs', 'requirements', 'acceptance criteria', 'task breakdown', 'đặc tả', 'dac ta', '仕様', '仕様書', '要件', '受け入れ条件', 'タスク分解', '仕様を作って', '仕様を作成'],
|
|
19
|
+
medium: ['requirement', 'ears', 'design doc', 'scope', '--validate', 'yêu cầu', 'yeu cau', 'phạm vi', 'pham vi', 'validate spec', 'kiểm tra spec', 'kiem tra spec', '要求', '設計書', 'スコープ', '検証', '仕様を確認'],
|
|
20
|
+
weak: ['tính năng mới', 'tinh nang moi', 'feature idea', 'user story', 'criteria', 'task list', '新機能', 'ユーザーストーリー', '基準', 'タスクリスト'],
|
|
21
|
+
negative: ['commit', 'push', 'bug', 'error', 'production', 'pptx', 'pdf'],
|
|
22
|
+
}),
|
|
23
|
+
route('hapo:develop', 'implementation from an approved spec or task list', 75, {
|
|
24
|
+
strong: ['develop', 'implement', 'implementation', 'theo spec', 'theo specs', 'approved spec', 'làm theo spec', 'lam theo spec', '実装', '開発', '仕様に沿って', '仕様どおり', '承認済み仕様'],
|
|
25
|
+
medium: ['build this', 'code this', 'start task', 'run task', 'thực hiện task', 'thuc hien task', 'phát triển', 'phat trien', 'bắt đầu implement', 'bat dau implement', 'vào code', 'vao code', '作って', 'コードを書いて', 'タスクを開始', 'タスクを実行', '開発して', '実装して'],
|
|
26
|
+
weak: ['triển khai', 'trien khai', 'đưa vào code', 'dua vao code', 'code feature', 'コード化', '機能を作る'],
|
|
27
|
+
negative: ['bug', 'debug', 'review', 'test only', 'commit'],
|
|
28
|
+
}),
|
|
29
|
+
route('hapo:test', 'test, verification, QA, or runtime validation', 70, {
|
|
30
|
+
strong: ['unit test', 'integration test', 'e2e', 'playwright', 'coverage', 'kiểm thử', 'kiem thu', '単体テスト', '結合テスト', 'カバレッジ', 'テストして'],
|
|
31
|
+
medium: ['test', 'tests', 'testing', 'qa', 'verify', 'verification', 'kiểm tra chạy', 'kiem tra chay', 'xác minh', 'xac minh', 'テスト', '検証', '確認', '動作確認'],
|
|
32
|
+
weak: ['assert', 'runtime proof', 'manual qa', 'end to end', 'smoke test', 'アサート', 'スモークテスト'],
|
|
33
|
+
negative: ['spec', 'requirements', 'commit', 'push'],
|
|
34
|
+
}),
|
|
35
|
+
route('hapo:code-review', 'code review, audit, security, or quality assessment', 68, {
|
|
36
|
+
strong: ['code review', 'security review', 'performance review', 'đánh giá code', 'danh gia code', 'コードレビュー', 'セキュリティレビュー', '性能レビュー'],
|
|
37
|
+
medium: ['review', 'audit', 'quality', 'đánh giá', 'danh gia', 'kiểm tra chất lượng', 'kiem tra chat luong', 'レビュー', '監査', '品質', '品質確認'],
|
|
38
|
+
weak: ['risk', 'maintainability', 'readability', 'vulnerability', 'lỗ hổng', 'lo hong', 'リスク', '保守性', '可読性', '脆弱性'],
|
|
39
|
+
negative: ['slide', 'pptx', 'commit and push'],
|
|
40
|
+
}),
|
|
41
|
+
route('hapo:git', 'git, commit, push, branch, tag, or pull request workflow', 65, {
|
|
42
|
+
strong: ['commit', 'push', 'pull request', 'git', 'đẩy lên', 'day len', 'コミット', 'プッシュ', 'プルリク', 'リリースして'],
|
|
43
|
+
medium: ['pr ', 'branch', 'tag', 'release', 'publish', 'merge', 'rebase', 'ブランチ', 'タグ', 'リリース', '公開', 'マージ', 'リベース'],
|
|
44
|
+
weak: ['origin', 'remote', 'checkout', 'stash', 'version bump', 'リモート', 'チェックアウト', 'スタッシュ', 'バージョン更新'],
|
|
45
|
+
negative: ['deploy', 'test', 'review only'],
|
|
46
|
+
}),
|
|
47
|
+
route('hapo:inspect', 'codebase discovery, file search, structure scan, or locating implementation areas', 64, {
|
|
48
|
+
strong: ['inspect', 'codebase scan', 'scan codebase', 'scan source', 'file discovery', 'find files', 'locate files', 'xem source', 'xem codebase', 'quét source', 'quet source', 'quét codebase', 'quet codebase', 'kiểm tra cấu trúc', 'kiem tra cau truc', 'コード構造', 'ソース確認', 'コードベース確認', 'ファイル探索', '構造を確認'],
|
|
49
|
+
medium: ['search files', 'find where', 'where is', 'project structure', 'code structure', 'repo structure', 'tìm file', 'tim file', 'tìm trong source', 'tim trong source', 'cấu trúc project', 'cau truc project', 'ở đâu', 'o dau', '関連ファイル', 'どこにある', 'プロジェクト構造', 'リポジトリ構造', '探して'],
|
|
50
|
+
weak: ['scan', 'inspect code', 'explore code', 'xem qua', 'xem giúp', '調べて', '確認して'],
|
|
51
|
+
negative: ['bug', 'error', 'lỗi', 'loi', 'fail', 'failure', 'production', 'hotfix', 'fix', 'sửa', 'sua', 'debug', 'develop', 'implement', 'test', 'commit', 'push', 'slide', 'pptx'],
|
|
52
|
+
}),
|
|
53
|
+
route('hapo:impact-analysis', 'impact analysis before changing existing behavior', 64, {
|
|
54
|
+
strong: ['impact analysis', 'blast radius', 'ảnh hưởng', 'anh huong', 'tác động', 'tac dong', '影響分析', '影響範囲', '影響', '副作用'],
|
|
55
|
+
medium: ['impact', 'liên quan những đâu', 'lien quan nhung dau', 'affected files', 'dependency impact', '関連箇所', '影響ファイル', '依存関係'],
|
|
56
|
+
weak: ['before changing', 'risk area', 'downstream', 'upstream', 'side effect', '変更前', 'リスク範囲', '下流', '上流'],
|
|
57
|
+
negative: ['slide', 'pptx'],
|
|
58
|
+
}),
|
|
59
|
+
route('hapo:frontend-design', 'UI/UX design, visual style, layout, or color system', 62, {
|
|
60
|
+
strong: ['ui design', 'visual style', 'color system', 'thiết kế giao diện', 'thiet ke giao dien', 'màu sắc', 'mau sac', 'uiデザイン', '画面デザイン', 'ビジュアルスタイル', '配色'],
|
|
61
|
+
medium: ['ux', 'layout', 'style', 'theme', 'responsive design', 'giao diện', 'giao dien', 'wireframe', 'レイアウト', 'スタイル', 'テーマ', 'レスポンシブデザイン', '画面', 'ワイヤーフレーム', '色'],
|
|
62
|
+
weak: ['palette', 'typography', 'spacing', 'polish ui', 'mockup', 'prototype', 'パレット', 'タイポグラフィ', '余白', 'モックアップ', 'プロトタイプ'],
|
|
63
|
+
negative: ['backend', 'api', 'database'],
|
|
64
|
+
}),
|
|
65
|
+
route('hapo:react-best-practices', 'React and Next.js performance patterns, rerender optimization, and Vercel best practices', 60, {
|
|
66
|
+
strong: ['react best practices', 'next.js best practices', 'vercel react best practices', 'react performance', 'next.js performance', 'optimize react', 'optimize next.js', 'tối ưu react', 'toi uu react', 'tối ưu next.js', 'toi uu next.js', 'reactベストプラクティス', 'next.jsベストプラクティス', 'react最適化', 'next.js最適化', 'react性能'],
|
|
67
|
+
medium: ['bundle optimization', 'bundle size', 'rerender optimization', 're-render optimization', 'data fetching', 'server component', 'client component', 'suspense', 'hydration', 'waterfall', 'usememo', 'usecallback', 'react cache', 'tối ưu rerender', 'toi uu rerender', 'tối ưu bundle', 'toi uu bundle', 'バンドル最適化', '再レンダー最適化', 'データ取得', 'サーバーコンポーネント', 'クライアントコンポーネント', 'ハイドレーション'],
|
|
68
|
+
weak: ['react', 'next.js', 'memo', 'rerender', 're-render', 'render performance', 'component performance', 'waterfalls', 'lazy state', 'dynamic import', 'react pattern', 'next.js pattern', 'レンダー性能', 'コンポーネント性能', '動的インポート'],
|
|
69
|
+
negative: ['backend', 'api', 'database', 'slide', 'pptx', 'commit', 'push'],
|
|
70
|
+
}),
|
|
71
|
+
route('hapo:frontend-development', 'frontend implementation work', 58, {
|
|
72
|
+
strong: ['react', 'next.js', 'vite', 'frontend', 'tailwind', 'web app', 'フロントエンド', 'webアプリ'],
|
|
73
|
+
medium: ['component', 'css', 'html', 'browser ui', 'client side', 'giao diện react', 'コンポーネント', 'ブラウザui', 'クライアント側', 'react画面'],
|
|
74
|
+
weak: ['state management', 'hook', 'form', 'table', 'dashboard ui', '状態管理', 'フック', 'フォーム', 'テーブル', 'ダッシュボードui'],
|
|
75
|
+
negative: ['best practices', 'performance', 'optimize', 'optimization', 'rerender', 're-render', 'tối ưu', 'toi uu', '再レンダー', '最適化', '性能', 'backend only', 'database only', 'slide', 'pptx'],
|
|
76
|
+
}),
|
|
77
|
+
route('hapo:backend-development', 'backend, API, database, or service implementation', 58, {
|
|
78
|
+
strong: ['backend', 'api', 'database', 'endpoint', 'server', 'service', 'バックエンド', 'データベース', 'エンドポイント', 'サーバー', 'サービス'],
|
|
79
|
+
medium: ['db', 'sql', 'postgres', 'mysql', 'migration', 'schema', 'worker', 'queue', 'マイグレーション', 'スキーマ', 'ワーカー', 'キュー'],
|
|
80
|
+
weak: ['controller', 'route handler', 'repository', 'model', 'auth service', 'コントローラ', 'ルートハンドラ', 'リポジトリ', 'モデル', '認証サービス'],
|
|
81
|
+
negative: ['frontend only', 'slide', 'pptx'],
|
|
82
|
+
}),
|
|
83
|
+
route('hapo:mobile-development', 'mobile app implementation', 56, {
|
|
84
|
+
strong: ['mobile', 'ios', 'android', 'react native', 'flutter', 'モバイル'],
|
|
85
|
+
medium: ['app store', 'play store', 'native app', 'mobile screen', 'アプリストア', 'playストア', 'ネイティブアプリ', 'モバイル画面'],
|
|
86
|
+
weak: ['gesture', 'push notification', 'offline sync', 'ジェスチャー', 'プッシュ通知', 'オフライン同期'],
|
|
87
|
+
negative: ['web only', 'desktop only'],
|
|
88
|
+
}),
|
|
89
|
+
route('hapo:devops', 'deployment, infrastructure, CI/CD, or operations work', 54, {
|
|
90
|
+
strong: ['deploy', 'deployment', 'docker', 'kubernetes', 'ci/cd', 'github actions', 'デプロイ', 'デプロイメント'],
|
|
91
|
+
medium: ['vercel', 'infra', 'infrastructure', 'devops', 'pipeline', 'environment variable', 'インフラ', 'パイプライン', '環境変数'],
|
|
92
|
+
weak: ['build server', 'container', 'helm', 'terraform', 'monitoring', 'ビルドサーバー', 'コンテナ', '監視'],
|
|
93
|
+
negative: ['slide', 'pptx', 'spec only'],
|
|
94
|
+
}),
|
|
95
|
+
route('hapo:generate-graph', 'diagram, graph, architecture map, or flow visualization', 52, {
|
|
96
|
+
strong: ['diagram', 'graph', 'mermaid', 'flowchart', 'architecture diagram', 'sơ đồ', 'so do', '図', '図解', 'ダイアグラム', 'グラフ', 'フローチャート', '構成図'],
|
|
97
|
+
medium: ['biểu đồ', 'bieu do', 'visualize', 'sequence diagram', 'data flow', '可視化', 'シーケンス図', 'データフロー'],
|
|
98
|
+
weak: ['mind map', 'dependency map', 'system map', 'マインドマップ', '依存関係図', 'システム図'],
|
|
99
|
+
negative: ['pptx', 'slide deck'],
|
|
100
|
+
}),
|
|
101
|
+
route('hapo:brainstorm', 'early ideation or unclear solution direction', 50, {
|
|
102
|
+
strong: ['brainstorm', 'ý tưởng', 'y tuong', 'phương án', 'phuong an', 'gợi ý', 'goi y', 'ブレスト', 'アイデア', '案', '提案して', '相談'],
|
|
103
|
+
medium: ['idea', 'ideas', 'approach', 'options', 'tradeoff', 'chủ đề', 'chu de', 'cần làm gì', 'can lam gi', 'アプローチ', '選択肢', 'トレードオフ', 'テーマ', '何をすれば'],
|
|
104
|
+
weak: ['proposal', 'strategy', 'plan options', 'explore', 'direction', 'seminar topic', '提案', '戦略', '方向性', '検討'],
|
|
105
|
+
negative: ['commit', 'push', 'bug', 'error'],
|
|
106
|
+
}),
|
|
107
|
+
route('hapo:research', 'technical research or best-practice lookup', 48, {
|
|
108
|
+
strong: ['research', 'best practice', 'tìm hiểu', 'tim hieu', 'nghiên cứu', 'nghien cuu', '調査', 'リサーチ', 'ベストプラクティス'],
|
|
109
|
+
medium: ['documentation', 'docs', 'compare tools', 'latest docs', 'official docs', 'ドキュメント', '比較', '最新ドキュメント', '公式ドキュメント'],
|
|
110
|
+
weak: ['investigate options', 'market scan', 'reference', 'source material', '選択肢を調べる', '参考資料', '資料'],
|
|
111
|
+
negative: ['commit', 'push'],
|
|
112
|
+
}),
|
|
113
|
+
route('hapo:pptx', 'presentation or PowerPoint work', 46, {
|
|
114
|
+
strong: ['pptx', 'powerpoint', 'slide deck', 'presentation deck', 'スライド資料', 'プレゼン資料'],
|
|
115
|
+
medium: ['slide', 'slides', 'deck', 'presentation', 'seminar slides', 'スライド', 'プレゼン', 'セミナー資料'],
|
|
116
|
+
weak: ['speaker notes', 'appendix', 'mục lục slide', 'muc luc slide', '発表ノート', '付録', '目次'],
|
|
117
|
+
negative: ['source code', 'api', 'database'],
|
|
118
|
+
}),
|
|
119
|
+
route('hapo:agent-browser', 'browser automation with snapshot refs, web interaction, recording, or Browserbase cloud browser workflows', 45, {
|
|
120
|
+
strong: ['agent-browser', 'browser automation', 'web automation', 'browserbase', 'cloud browser', 'snapshot refs', 'browser snapshot', 'automate browser', 'tự động trình duyệt', 'tu dong trinh duyet', 'tự động thao tác trình duyệt', 'tu dong thao tac trinh duyet', 'ブラウザ自動化', 'クラウドブラウザ', 'ブラウザ操作', 'ブラウザスナップショット'],
|
|
121
|
+
medium: ['open url', 'navigate site', 'click in browser', 'fill form in browser', 'record browser', 'browser session', 'multi tab', 'browser test session', 'mở website', 'mo website', 'truy cập website', 'truy cap website', 'click trên web', 'click tren web', 'điền form web', 'dien form web', 'サイトを開く', 'ブラウザで開く', 'フォーム入力', 'クリック操作', '録画'],
|
|
122
|
+
weak: ['click button', 'fill form', 'open site', 'web session', 'browser ref', 'viewport', 'cookies', 'localstorage', 'ボタンをクリック', 'ビューポート', 'クッキー'],
|
|
123
|
+
negative: ['attached screenshot', 'ảnh đính kèm', 'anh dinh kem', '画像添付', 'source code', 'codebase', 'commit', 'push', 'pptx', 'pdf'],
|
|
124
|
+
}),
|
|
125
|
+
route('hapo:pdf', 'PDF reading, extraction, or generation', 44, {
|
|
126
|
+
strong: ['pdf'], medium: ['export pdf', 'read pdf', 'extract pdf', 'pdf出力', 'pdfを読む', 'pdf抽出'], weak: ['page render', 'ページレンダー'], negative: [],
|
|
127
|
+
}),
|
|
128
|
+
route('hapo:docx', 'Word document work', 42, {
|
|
129
|
+
strong: ['docx', 'word document', 'word file', 'word文書', 'wordファイル'], medium: ['word'], weak: ['tracked changes', 'document file', '変更履歴', '文書ファイル'], negative: [],
|
|
130
|
+
}),
|
|
131
|
+
route('hapo:xlsx', 'spreadsheet or Excel work', 42, {
|
|
132
|
+
strong: ['xlsx', 'excel', 'spreadsheet', 'スプレッドシート'], medium: ['csv', 'sheet', 'workbook', 'シート', 'ワークブック'], weak: ['formula', 'pivot table', '数式', 'ピボットテーブル'], negative: [],
|
|
133
|
+
}),
|
|
134
|
+
route('hapo:ai-multimodal', 'image, video, audio, or multimodal artifact analysis', 40, {
|
|
135
|
+
strong: ['screenshot', 'video', 'audio', 'multimodal', 'ảnh', 'anh', 'hình', 'hinh', 'スクリーンショット', '動画', '音声', '画像', '写真'],
|
|
136
|
+
medium: ['image', 'screen capture', 'recording', 'file attached', 'イメージ', '画面キャプチャ', '録画', '添付ファイル'],
|
|
137
|
+
weak: ['visual', 'describe image', 'ocr', 'ビジュアル', '画像説明'],
|
|
138
|
+
negative: ['pptx', 'pdf export'],
|
|
139
|
+
}),
|
|
140
|
+
];
|
|
141
|
+
|
|
142
|
+
function route(skill, reason, priority, signals) {
|
|
143
|
+
return { skill, reason, priority, signals };
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function normalize(value) {
|
|
147
|
+
return String(value || '')
|
|
148
|
+
.normalize('NFKC')
|
|
149
|
+
.normalize('NFD')
|
|
150
|
+
.replace(/[\u0300-\u036f]/g, '')
|
|
151
|
+
.normalize('NFC')
|
|
152
|
+
.toLowerCase();
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function scoreRoute(prompt, routeItem) {
|
|
156
|
+
const normalized = normalize(prompt);
|
|
157
|
+
const matched = [];
|
|
158
|
+
let score = 0;
|
|
159
|
+
for (const [bucket, weight] of Object.entries(WEIGHTS)) {
|
|
160
|
+
for (const term of routeItem.signals[bucket] || []) {
|
|
161
|
+
if (!normalized.includes(normalize(term))) continue;
|
|
162
|
+
score += weight;
|
|
163
|
+
matched.push({ bucket, term, weight });
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
return { ...routeItem, score, matched, confidence: confidence(score) };
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function confidence(score) {
|
|
170
|
+
if (score >= 12) return 'high';
|
|
171
|
+
if (score >= 7) return 'medium';
|
|
172
|
+
if (score >= MIN_SCORE) return 'low';
|
|
173
|
+
return 'none';
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function findRoute(prompt) {
|
|
177
|
+
const candidates = ROUTES
|
|
178
|
+
.map((routeItem, index) => ({ ...scoreRoute(prompt, routeItem), index }))
|
|
179
|
+
.filter((routeItem) => routeItem.score >= MIN_SCORE)
|
|
180
|
+
.sort((a, b) => b.score - a.score || b.priority - a.priority || a.index - b.index);
|
|
181
|
+
return candidates[0] || null;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
module.exports = {
|
|
185
|
+
MIN_SCORE,
|
|
186
|
+
ROUTES,
|
|
187
|
+
findRoute,
|
|
188
|
+
normalize,
|
|
189
|
+
scoreRoute,
|
|
190
|
+
};
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Copyright (c) 2026 Haposoft. MIT License.
|
|
4
|
+
*
|
|
5
|
+
* UserPromptSubmit Hook - skill-router.cjs
|
|
6
|
+
*
|
|
7
|
+
* Adds a deterministic CafeKit skill suggestion for natural-language prompts.
|
|
8
|
+
* It never overrides explicit slash commands; it only injects a short routing hint.
|
|
9
|
+
*
|
|
10
|
+
* Exit: 0 always (fail-open)
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
const fs = require('fs');
|
|
14
|
+
const path = require('path');
|
|
15
|
+
|
|
16
|
+
function logCrash(error) {
|
|
17
|
+
try {
|
|
18
|
+
const dir = path.join(__dirname, '.logs');
|
|
19
|
+
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
20
|
+
fs.appendFileSync(
|
|
21
|
+
path.join(dir, 'hook-log.jsonl'),
|
|
22
|
+
JSON.stringify({
|
|
23
|
+
ts: new Date().toISOString(),
|
|
24
|
+
hook: 'skill-router',
|
|
25
|
+
status: 'crash',
|
|
26
|
+
error: error.message,
|
|
27
|
+
}) + '\n'
|
|
28
|
+
);
|
|
29
|
+
} catch (_) {}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
try {
|
|
33
|
+
const { isHookEnabled } = require('./lib/config.cjs');
|
|
34
|
+
const { findRoute } = require('./lib/skill-router-routes.cjs');
|
|
35
|
+
|
|
36
|
+
function isExplicitCommand(prompt) {
|
|
37
|
+
const trimmed = prompt.trim();
|
|
38
|
+
return trimmed.startsWith('/') || /^hapo:[a-z-]+/i.test(trimmed);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (!isHookEnabled('skill-router')) process.exit(0);
|
|
42
|
+
|
|
43
|
+
const stdin = fs.readFileSync(0, 'utf8').trim();
|
|
44
|
+
if (!stdin) process.exit(0);
|
|
45
|
+
|
|
46
|
+
const payload = JSON.parse(stdin);
|
|
47
|
+
const prompt = payload.prompt || '';
|
|
48
|
+
if (!prompt || isExplicitCommand(prompt)) process.exit(0);
|
|
49
|
+
|
|
50
|
+
const route = findRoute(prompt);
|
|
51
|
+
if (!route) process.exit(0);
|
|
52
|
+
|
|
53
|
+
const skillDir = route.skill.replace(/^hapo:/, '');
|
|
54
|
+
const lines = [
|
|
55
|
+
'## CafeKit Skill Router',
|
|
56
|
+
`- Suggested skill: \`${route.skill}\``,
|
|
57
|
+
`- Why: ${route.reason}.`,
|
|
58
|
+
`- Confidence: ${route.confidence} (score ${route.score}).`,
|
|
59
|
+
`- Action: before acting, read \`.claude/skills/${skillDir}/SKILL.md\` and follow that workflow.`,
|
|
60
|
+
'- If the user explicitly names another workflow or asks for direct answering only, follow the user request.',
|
|
61
|
+
];
|
|
62
|
+
|
|
63
|
+
console.log(lines.join('\n'));
|
|
64
|
+
process.exit(0);
|
|
65
|
+
} catch (error) {
|
|
66
|
+
try { logCrash(error); } catch (_) {}
|
|
67
|
+
process.exit(0);
|
|
68
|
+
}
|
|
@@ -68,6 +68,7 @@
|
|
|
68
68
|
},
|
|
69
69
|
"runtime": {
|
|
70
70
|
"files": [
|
|
71
|
+
"gitignore",
|
|
71
72
|
"runtime.json",
|
|
72
73
|
"status.cjs",
|
|
73
74
|
"hooks/session.cjs",
|
|
@@ -77,6 +78,7 @@
|
|
|
77
78
|
"hooks/privacy-block.cjs",
|
|
78
79
|
"hooks/inspect-block.cjs",
|
|
79
80
|
"hooks/rules.cjs",
|
|
81
|
+
"hooks/skill-router.cjs",
|
|
80
82
|
"hooks/spec-state.cjs",
|
|
81
83
|
"hooks/state.cjs",
|
|
82
84
|
"hooks/lib/color.cjs",
|
|
@@ -85,7 +87,8 @@
|
|
|
85
87
|
"hooks/lib/git.cjs",
|
|
86
88
|
"hooks/lib/config.cjs",
|
|
87
89
|
"hooks/lib/context.cjs",
|
|
88
|
-
"hooks/lib/detect.cjs"
|
|
90
|
+
"hooks/lib/detect.cjs",
|
|
91
|
+
"hooks/lib/skill-router-routes.cjs"
|
|
89
92
|
]
|
|
90
93
|
},
|
|
91
94
|
"settings": {
|
|
@@ -86,23 +86,31 @@ function extractRequirementIds(requirementsText) {
|
|
|
86
86
|
}
|
|
87
87
|
|
|
88
88
|
function validateTaskSections(taskPath, content, errors) {
|
|
89
|
-
const hasContext =
|
|
90
|
-
|
|
91
|
-
hasHeading(content, 'Objective') ||
|
|
92
|
-
hasHeading(content, 'Goal');
|
|
89
|
+
const hasContext = hasHeading(content, 'Context');
|
|
90
|
+
const hasConstraints = hasHeading(content, 'Constraints');
|
|
93
91
|
const hasSteps =
|
|
94
92
|
hasHeading(content, 'Steps') || hasHeading(content, 'Implementation Steps');
|
|
95
93
|
const hasRequirements =
|
|
96
94
|
hasHeading(content, 'Requirements') || /_Requirements:\s*[^_\n]+_/i.test(content);
|
|
95
|
+
const hasRelatedFiles = hasHeading(content, 'Related Files');
|
|
96
|
+
const hasCompletionCriteria = hasHeading(content, 'Completion Criteria');
|
|
97
97
|
const hasEvidence =
|
|
98
98
|
hasHeading(content, 'Evidence') ||
|
|
99
99
|
hasHeading(content, 'Task Test Plan & Verification Evidence') ||
|
|
100
100
|
hasHeading(content, 'Verification & Evidence');
|
|
101
|
+
const hasRiskAssessment = hasHeading(content, 'Risk Assessment');
|
|
101
102
|
|
|
102
|
-
if (!hasContext) errors.push(`${taskPath}: missing Context
|
|
103
|
+
if (!hasContext) errors.push(`${taskPath}: missing Context`);
|
|
104
|
+
if (!hasConstraints) errors.push(`${taskPath}: missing Constraints`);
|
|
103
105
|
if (!hasSteps) errors.push(`${taskPath}: missing Steps/Implementation Steps`);
|
|
104
106
|
if (!hasRequirements) errors.push(`${taskPath}: missing Requirements mapping`);
|
|
107
|
+
if (!hasRelatedFiles) errors.push(`${taskPath}: missing Related Files`);
|
|
108
|
+
if (!hasCompletionCriteria) errors.push(`${taskPath}: missing Completion Criteria`);
|
|
105
109
|
if (!hasEvidence) errors.push(`${taskPath}: missing Evidence or task test plan`);
|
|
110
|
+
if (!hasRiskAssessment) errors.push(`${taskPath}: missing Risk Assessment`);
|
|
111
|
+
if (hasEvidence && !/Runtime reachability verification/i.test(content)) {
|
|
112
|
+
errors.push(`${taskPath}: missing Runtime reachability verification`);
|
|
113
|
+
}
|
|
106
114
|
}
|
|
107
115
|
|
|
108
116
|
function validateSpec(specDir) {
|
|
@@ -187,6 +195,28 @@ function validateSpec(specDir) {
|
|
|
187
195
|
errors.push('tasks/: feature work cannot be entirely R0; reserve R0 for shared foundation tasks');
|
|
188
196
|
}
|
|
189
197
|
|
|
198
|
+
const validationRecommended = spec.design_context?.validation_recommended === true;
|
|
199
|
+
if (taskFiles.length >= 5 && !validationRecommended) {
|
|
200
|
+
errors.push('spec.json.design_context.validation_recommended: must be true for specs with 5+ task files');
|
|
201
|
+
}
|
|
202
|
+
if (
|
|
203
|
+
(validationRecommended || taskFiles.length >= 5) &&
|
|
204
|
+
spec.ready_for_implementation === true &&
|
|
205
|
+
spec.validation?.status !== 'completed'
|
|
206
|
+
) {
|
|
207
|
+
errors.push(
|
|
208
|
+
'spec.json.ready_for_implementation: cannot be true when validation is recommended but validation.status is not completed',
|
|
209
|
+
);
|
|
210
|
+
}
|
|
211
|
+
if (spec.validation?.status === 'completed') {
|
|
212
|
+
if (!spec.timestamps?.validation_done) {
|
|
213
|
+
errors.push('spec.json.timestamps.validation_done: required when validation.status is completed');
|
|
214
|
+
}
|
|
215
|
+
if (taskFiles.length >= 5 && !spec.timestamps?.review_done) {
|
|
216
|
+
errors.push('spec.json.timestamps.review_done: required for 5+ task specs after validation');
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
190
220
|
const requirementsPath = path.join(specDir, 'requirements.md');
|
|
191
221
|
const designPath = path.join(specDir, 'design.md');
|
|
192
222
|
const researchPath = path.join(specDir, 'research.md');
|
|
@@ -42,6 +42,10 @@
|
|
|
42
42
|
"type": "command",
|
|
43
43
|
"command": "node \"$CLAUDE_PROJECT_DIR/.claude/hooks/rules.cjs\""
|
|
44
44
|
},
|
|
45
|
+
{
|
|
46
|
+
"type": "command",
|
|
47
|
+
"command": "node \"$CLAUDE_PROJECT_DIR/.claude/hooks/skill-router.cjs\""
|
|
48
|
+
},
|
|
45
49
|
{
|
|
46
50
|
"type": "command",
|
|
47
51
|
"command": "node \"$CLAUDE_PROJECT_DIR/.claude/hooks/spec-state.cjs\""
|
|
@@ -16,17 +16,37 @@ Debugging is diagnosis, not repair. Find the source of the failure before changi
|
|
|
16
16
|
- `--frontend` - Include browser console, screenshot, accessibility tree, network, and responsive checks
|
|
17
17
|
- `--perf` - Include baseline measurements, bottleneck layer, profiling, and before/after targets
|
|
18
18
|
|
|
19
|
-
Default: systematic diagnosis with no product-code edits.
|
|
20
|
-
|
|
21
|
-
<
|
|
22
|
-
|
|
19
|
+
Default: systematic diagnosis with no product-code edits, scout first.
|
|
20
|
+
|
|
21
|
+
<DIAGNOSTIC-ONLY-GATE>
|
|
22
|
+
`hapo:debug` is read-only for product code.
|
|
23
|
+
Do NOT edit product code, apply fixes, create migrations, or add regression tests as implementation.
|
|
24
|
+
Do NOT change config, dependency versions, generated assets, or test snapshots to make the failure disappear.
|
|
25
|
+
Temporary instrumentation is allowed only when it is the minimal way to observe hidden state; record the file/line, capture the proof, remove it before finishing, and report `Temporary instrumentation: removed`.
|
|
26
|
+
</DIAGNOSTIC-ONLY-GATE>
|
|
27
|
+
|
|
28
|
+
<HARD-GATE-SCOUT-FIRST>
|
|
29
|
+
Before hypotheses, inspect the actual codebase context.
|
|
30
|
+
You must identify:
|
|
31
|
+
- project type, language, framework, runtime, and test runner
|
|
32
|
+
- affected files/modules and exact symptom location
|
|
33
|
+
- direct callers, dependents, and data/config boundaries
|
|
34
|
+
- related tests and reproduction commands
|
|
35
|
+
- recent commits touching affected paths
|
|
36
|
+
- adjacent known-good implementation patterns
|
|
37
|
+
|
|
38
|
+
After scout, provide a 3-6 bullet codebase-context summary before evidence capture.
|
|
39
|
+
Do not ask generic questions before this step unless the issue cannot be located from the prompt or repository.
|
|
40
|
+
</HARD-GATE-SCOUT-FIRST>
|
|
41
|
+
|
|
42
|
+
<ROOT-CAUSE-GATE>
|
|
23
43
|
Do NOT recommend a fix until the root-cause contract is complete.
|
|
24
44
|
Do NOT stop at the first plausible explanation. Test hypotheses against evidence.
|
|
25
45
|
If 2+ hypotheses are refuted, change strategy before continuing.
|
|
26
|
-
If evidence is insufficient, report `Root cause: unknown`
|
|
27
|
-
</
|
|
46
|
+
If evidence is insufficient, report `Root cause: unknown`, `Missing Evidence`, and `Next Diagnostic Action`; do not hand off to `hapo:hotfix` as ready.
|
|
47
|
+
</ROOT-CAUSE-GATE>
|
|
28
48
|
|
|
29
|
-
|
|
49
|
+
If the user asks to fix while still inside `hapo:debug`, finish the debug report first. Then hand off only the completed root-cause contract to `hapo:hotfix`.
|
|
30
50
|
|
|
31
51
|
## Process Flow
|
|
32
52
|
|
|
@@ -53,15 +73,18 @@ flowchart TD
|
|
|
53
73
|
Understand the affected code before forming hypotheses.
|
|
54
74
|
|
|
55
75
|
**Action:** Activate `hapo:inspect` for the relevant scope.
|
|
76
|
+
If `hapo:inspect` is unavailable, use direct read-only reconnaissance (`rg`, file reads, test discovery, and `git log`) and state that fallback.
|
|
56
77
|
|
|
57
78
|
**Checklist:**
|
|
79
|
+
- [ ] Project type, language, framework, runtime, and test runner identified
|
|
58
80
|
- [ ] Affected files and modules identified
|
|
59
81
|
- [ ] Direct dependencies and call paths mapped
|
|
82
|
+
- [ ] Inputs/outputs, data boundaries, and config/env boundaries mapped
|
|
60
83
|
- [ ] Related tests located
|
|
61
84
|
- [ ] Recent changes checked: `git log --oneline -10 -- <affected-files>`
|
|
62
85
|
- [ ] Existing working examples or adjacent patterns identified
|
|
63
86
|
|
|
64
|
-
**Output:** `✓ Step 1: Scouted - [N] files, [M] deps, [K] tests`
|
|
87
|
+
**Output:** `✓ Step 1: Scouted - [N] files, [M] deps, [K] tests` plus a 3-6 bullet context summary.
|
|
65
88
|
|
|
66
89
|
---
|
|
67
90
|
|
|
@@ -77,9 +100,9 @@ Create a baseline that can later prove whether the issue changed.
|
|
|
77
100
|
- Environment facts: runtime, dependency versions, OS, browser, CI runner, config
|
|
78
101
|
- Whether the issue reproduces consistently or intermittently
|
|
79
102
|
|
|
80
|
-
For frontend issues, use
|
|
81
|
-
For CI/log issues, use
|
|
82
|
-
For performance issues, use
|
|
103
|
+
For frontend issues, use `.claude/references/debugger/frontend-verification.md`.
|
|
104
|
+
For CI/log issues, use `.claude/references/debugger/log-ci-analysis.md`.
|
|
105
|
+
For performance issues, use `.claude/references/debugger/performance-diagnostics.md`.
|
|
83
106
|
|
|
84
107
|
**Output:** `✓ Step 2: Evidence captured - baseline command/symptom recorded`
|
|
85
108
|
|
|
@@ -115,7 +138,7 @@ Result: confirmed | refuted | inconclusive
|
|
|
115
138
|
Rules:
|
|
116
139
|
- Never batch unrelated changes as a test.
|
|
117
140
|
- Prefer read-only evidence: logs, grep, stack traces, DB queries, browser traces.
|
|
118
|
-
- For flaky async tests, use
|
|
141
|
+
- For flaky async tests, use `.claude/references/debugger/condition-based-waiting.md`.
|
|
119
142
|
- If 2+ hypotheses are refuted, use inversion: ask what evidence would make the current explanation impossible.
|
|
120
143
|
|
|
121
144
|
**Output:** `✓ Step 4: Hypotheses tested - [confirmed/refuted counts]`
|
|
@@ -156,7 +179,7 @@ Prepare the handoff to `hapo:hotfix` or the user.
|
|
|
156
179
|
- Affected-module tests
|
|
157
180
|
- Typecheck/lint/build commands when relevant
|
|
158
181
|
- UI screenshot/console/network checks when relevant
|
|
159
|
-
- Side-effect sweep from
|
|
182
|
+
- Side-effect sweep from `.claude/references/debugger/side-effect-gate.md`
|
|
160
183
|
|
|
161
184
|
**Output:** `✓ Step 6: Verification planned - [commands/scenarios]`
|
|
162
185
|
|
|
@@ -189,9 +212,18 @@ Prepare the handoff to `hapo:hotfix` or the user.
|
|
|
189
212
|
- Regression guard:
|
|
190
213
|
- Side-effect sweep:
|
|
191
214
|
|
|
215
|
+
### Temporary Instrumentation
|
|
216
|
+
- none | removed: [file:line, purpose, proof captured]
|
|
217
|
+
|
|
192
218
|
### Recommended Fix Direction
|
|
193
219
|
[Smallest root-cause fix, or "insufficient evidence"]
|
|
194
220
|
|
|
221
|
+
### Missing Evidence
|
|
222
|
+
- [Only when root cause is unknown]
|
|
223
|
+
|
|
224
|
+
### Next Diagnostic Action
|
|
225
|
+
- [Only when root cause is unknown]
|
|
226
|
+
|
|
195
227
|
### Unresolved Questions
|
|
196
228
|
- [Only if any]
|
|
197
229
|
```
|
|
@@ -199,18 +231,19 @@ Prepare the handoff to `hapo:hotfix` or the user.
|
|
|
199
231
|
## Relationship To Hotfix
|
|
200
232
|
|
|
201
233
|
- Use `hapo:debug` to determine what is wrong.
|
|
202
|
-
- Use `hapo:hotfix` to change code after the root-cause contract is complete.
|
|
234
|
+
- Use `hapo:hotfix` to change code only after the root-cause contract is complete.
|
|
235
|
+
- A `Root cause: unknown` report is not ready for hotfix; continue diagnosis or ask for the missing artifact.
|
|
203
236
|
- If `hapo:hotfix` verification fails, return to `hapo:debug` with the new evidence.
|
|
204
237
|
|
|
205
238
|
## References
|
|
206
239
|
|
|
207
240
|
Load as needed:
|
|
208
|
-
-
|
|
209
|
-
-
|
|
210
|
-
-
|
|
211
|
-
-
|
|
212
|
-
-
|
|
213
|
-
-
|
|
214
|
-
-
|
|
215
|
-
-
|
|
216
|
-
-
|
|
241
|
+
- `.claude/references/debugger/core-philosophy.md` - Anti-guessing discipline
|
|
242
|
+
- `.claude/references/debugger/root-cause-tracing.md` - Backward trace to origin
|
|
243
|
+
- `.claude/references/debugger/verification-protocol.md` - Fresh evidence requirements
|
|
244
|
+
- `.claude/references/debugger/log-ci-analysis.md` - Logs and CI/CD failure analysis
|
|
245
|
+
- `.claude/references/debugger/parallel-agent-hydration.md` - Parallel reconnaissance
|
|
246
|
+
- `.claude/references/debugger/frontend-verification.md` - Browser/UI verification
|
|
247
|
+
- `.claude/references/debugger/performance-diagnostics.md` - Performance investigation
|
|
248
|
+
- `.claude/references/debugger/condition-based-waiting.md` - Flaky async test diagnosis
|
|
249
|
+
- `.claude/references/debugger/side-effect-gate.md` - Regression and blast-radius checks
|