@nahisaho/shikigami 2.0.5 → 2.0.6
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/CHANGELOG.md +25 -0
- package/README.md +117 -140
- package/mcp-server/package.json +1 -1
- package/package.json +1 -1
- package/scripts/build-cowork-plugin.js +437 -165
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,24 @@ All notable changes to SHIKIGAMI will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [2.0.6] - 2026-05-18
|
|
9
|
+
|
|
10
|
+
### Changed
|
|
11
|
+
|
|
12
|
+
- **Coworkプラグインビルダーをゼロから再構築**
|
|
13
|
+
- 公式ドキュメント「プラグインをゼロからビルドする」に完全準拠
|
|
14
|
+
- ASKILL検証コード(ASKILL-M001〜P008)によるバリデーション
|
|
15
|
+
- コンパニオンファイルパス検証(隠しファイル、パストラバーサル、Windows予約名、安全文字チェック)
|
|
16
|
+
- Cowork用SKILL.md自動リライト(cowork.category, cowork.icon, metadata追加)
|
|
17
|
+
- パス参照の自動更新(統合後のreferences/パスに変換)
|
|
18
|
+
- Additional Resources セクション自動生成
|
|
19
|
+
- セキュリティ改善: execSync → execFileSync(シェルインジェクション防止)
|
|
20
|
+
- SKILL.md本文サイズ警告(3000語超で警告)
|
|
21
|
+
- 統合ファイルの5MB超過時チャンキング対応
|
|
22
|
+
- マニフェスト検証(GUID形式、重複folder、スキル数上限)
|
|
23
|
+
- クロスプラットフォーム互換性表示(GitHub Copilot CLI / VS Code / Claude Code / Gemini CLI)
|
|
24
|
+
- 参照: https://learn.microsoft.com/microsoft-365/copilot/cowork/cowork-plugin-development
|
|
25
|
+
|
|
8
26
|
## [2.0.5] - 2026-05-18
|
|
9
27
|
|
|
10
28
|
### Changed
|
|
@@ -16,6 +34,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
16
34
|
- manifest.json自動生成、アイコン生成、スキル検証機能
|
|
17
35
|
- Companionファイル20制限への自動統合(63ファイル→14ファイル等)
|
|
18
36
|
- 参照: https://learn.microsoft.com/microsoft-365/copilot/cowork/cowork-plugin-development
|
|
37
|
+
- **README.md全面刷新**
|
|
38
|
+
- インストールコマンド修正(`@nahisaho/shikigami`)
|
|
39
|
+
- 全19プロンプトをカテゴリ別に一覧化
|
|
40
|
+
- Agent Skills: npmパッケージ含有4スキルに正確化
|
|
41
|
+
- 50+コンサルティングフレームワーク一覧を追加
|
|
42
|
+
- アーキテクチャ図をv2.0.5に更新
|
|
43
|
+
- CLIコマンド一覧を追加
|
|
19
44
|
|
|
20
45
|
### Removed
|
|
21
46
|
|
package/README.md
CHANGED
|
@@ -1,90 +1,66 @@
|
|
|
1
1
|
# SHIKIGAMI
|
|
2
2
|
|
|
3
|
-
**GitHub Copilot Agent Skills for Deep Research &
|
|
3
|
+
**GitHub Copilot Agent Skills for Deep Research & Consulting**
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
50+のコンサルティングフレームワーク(SWOT、PEST、5Forces、BCG Matrix、ピラミッド原則等)を活用した深層リサーチ自動化Agent Skillsパッケージです。GitHub Copilot CLI と連携し、Copilot CLI の組み込み機能を活用しつつ、SHIKIGAMI独自のリサーチ・コンサルティング機能を提供します。
|
|
6
6
|
|
|
7
7
|
## 🎯 できること
|
|
8
8
|
|
|
9
|
-
- **対話的目的探索**:
|
|
10
|
-
- **Deep Research**:
|
|
11
|
-
-
|
|
12
|
-
- **高品質レポート生成**:
|
|
9
|
+
- **対話的目的探索**: ユーザーの表層的な依頼から「真の目的」を5 Whys/JTBDで探索
|
|
10
|
+
- **Deep Research**: Think→Report→Actionサイクルに基づく反復的深層リサーチ
|
|
11
|
+
- **コンサルティング・フレームワーク分析**: 50+フレームワーク(SWOT、PEST、5Forces、BCG Matrix、7S、ピラミッド原則等)を適用
|
|
12
|
+
- **高品質レポート生成**: ハルシネーション防止、ソース追跡、引用管理、Qiita記事対応
|
|
13
13
|
- **マルチポップ探索**: リサーチ中の分岐点を自動検出し、複数パスを同時探索
|
|
14
14
|
- **ベクトル検索**: 過去のリサーチ資産をセマンティック検索で再利用
|
|
15
|
-
-
|
|
15
|
+
- **Copilot Cowork連携**: M365 Copilot Coworkプラグインとしてエクスポート可能
|
|
16
16
|
|
|
17
17
|
## 📦 インストール
|
|
18
18
|
|
|
19
19
|
```bash
|
|
20
|
-
npm install @nahisaho/shikigami
|
|
20
|
+
npm install @nahisaho/shikigami
|
|
21
|
+
npx shikigami init
|
|
21
22
|
```
|
|
22
23
|
|
|
23
|
-
|
|
24
|
+
セットアップ後:
|
|
24
25
|
|
|
25
26
|
```bash
|
|
26
|
-
|
|
27
|
+
cd mcp-server && npm install && npm run build
|
|
27
28
|
```
|
|
28
29
|
|
|
29
30
|
## 🏗️ アーキテクチャ
|
|
30
31
|
|
|
31
|
-
SHIKIGAMI v2.0.0 は GitHub Copilot CLI の組み込み機能を最大限活用する設計に移行しました。
|
|
32
|
-
|
|
33
32
|
```
|
|
34
33
|
┌─────────────────────────────────────────────────┐
|
|
35
34
|
│ GitHub Copilot CLI │
|
|
36
35
|
│ web_search / web_fetch / store_memory / grep │
|
|
37
36
|
│ explore / research / rubber-duck agents │
|
|
38
|
-
│ /session / /context / /compact │
|
|
39
37
|
├─────────────────────────────────────────────────┤
|
|
40
|
-
│ SHIKIGAMI v2.0.
|
|
38
|
+
│ SHIKIGAMI v2.0.6 │
|
|
41
39
|
│ ┌──────────────┐ ┌───────────────────────┐ │
|
|
42
40
|
│ │ Agent Skills │ │ MCP Server (10 tools) │ │
|
|
43
|
-
│ │ (
|
|
41
|
+
│ │ (4 skills) │ │ embed / similarity │ │
|
|
44
42
|
│ │ │ │ semantic_search │ │
|
|
45
|
-
│ │
|
|
46
|
-
│ │
|
|
47
|
-
│ │
|
|
48
|
-
│ │
|
|
49
|
-
│
|
|
43
|
+
│ │ Planner │ │ branch_detect/eval/ │ │
|
|
44
|
+
│ │ Deep Research │ │ select │ │
|
|
45
|
+
│ │ Framework │ │ set/get_project │ │
|
|
46
|
+
│ │ Writing │ │ save_prompt/research │ │
|
|
47
|
+
│ ├──────────────┤ └───────────────────────┘ │
|
|
48
|
+
│ │ 19 Prompts │ │
|
|
49
|
+
│ └──────────────┘ │
|
|
50
|
+
├─────────────────────────────────────────────────┤
|
|
51
|
+
│ Copilot Cowork Plugin (.zip) │
|
|
52
|
+
│ npx shikigami cowork で M365 プラグイン生成 │
|
|
50
53
|
└─────────────────────────────────────────────────┘
|
|
51
54
|
```
|
|
52
55
|
|
|
53
|
-
## 🔧 Agent Skills(
|
|
54
|
-
|
|
55
|
-
### リサーチ・コンサルティング
|
|
56
|
+
## 🔧 Agent Skills(4スキル)
|
|
56
57
|
|
|
57
58
|
| スキル | 説明 |
|
|
58
59
|
|--------|------|
|
|
59
60
|
| `shikigami-planner` | 対話的目的探索・5 Whys・JTBD・リサーチ計画立案 |
|
|
60
61
|
| `shikigami-deep-research` | Think→Report→Actionサイクルの反復的深層リサーチ |
|
|
61
|
-
| `shikigami-consulting-framework` |
|
|
62
|
-
| `shikigami-writing` |
|
|
63
|
-
|
|
64
|
-
### 品質・検証
|
|
65
|
-
|
|
66
|
-
| スキル | 説明 |
|
|
67
|
-
|--------|------|
|
|
68
|
-
| `build-fix` | ビルドエラーを分類・反復修正(TypeScript/ESLint/依存関係) |
|
|
69
|
-
| `checkpoint` | セーフポイントの作成・復元・比較(Git統合) |
|
|
70
|
-
| `e2e-runner` | Playwrightを使用したE2Eテスト生成・実行 |
|
|
71
|
-
| `eval-harness` | pass@kメトリクスでAIコード生成品質を評価 |
|
|
72
|
-
| `verification-loop` | Build→Type→Lint→Test→Security→Diff の6フェーズ検証 |
|
|
73
|
-
|
|
74
|
-
### MUSUBIX SDD
|
|
75
|
-
|
|
76
|
-
| スキル | 説明 |
|
|
77
|
-
|--------|------|
|
|
78
|
-
| `musubix-sdd-workflow` | 10憲法条項に従った仕様駆動開発ガイド |
|
|
79
|
-
| `musubix-domain-inference` | 自動ドメイン検出・コンポーネント推論 |
|
|
80
|
-
| `musubix-code-generation` | 設計仕様からのコード生成 |
|
|
81
|
-
| `musubix-test-generation` | TDD/BDDによるテスト生成 |
|
|
82
|
-
| `musubix-adr-generation` | Architecture Decision Records作成 |
|
|
83
|
-
| `musubix-best-practices` | 学習済み17ベストプラクティスガイド |
|
|
84
|
-
| `musubix-c4-design` | C4モデル設計ドキュメント作成 |
|
|
85
|
-
| `musubix-technical-writing` | 技術ドキュメント作成 |
|
|
86
|
-
| `musubix-ears-validation` | EARS形式要件の検証・作成 |
|
|
87
|
-
| `musubix-traceability` | 要件・設計・コード・テスト間のトレーサビリティ管理 |
|
|
62
|
+
| `shikigami-consulting-framework` | 50+コンサルティングフレームワーク分析 |
|
|
63
|
+
| `shikigami-writing` | レポート生成・ハルシネーション検出・引用管理・Qiita対応 |
|
|
88
64
|
|
|
89
65
|
## 🔌 MCP Server(10ツール)
|
|
90
66
|
|
|
@@ -100,112 +76,72 @@ SHIKIGAMI v2.0.0 は GitHub Copilot CLI の組み込み機能を最大限活用
|
|
|
100
76
|
| `branch_evaluate` | 分岐パスのスコアリング・評価 |
|
|
101
77
|
| `branch_select` | 最適な探索パスの選択 |
|
|
102
78
|
|
|
103
|
-
## 📝 Prompts
|
|
79
|
+
## 📝 Prompts(19プロンプト)
|
|
80
|
+
|
|
81
|
+
### コア
|
|
104
82
|
|
|
105
83
|
| プロンプト | 説明 |
|
|
106
84
|
|-----------|------|
|
|
85
|
+
| `shikigami-full-research` | 統合リサーチ(Planner→Research→Framework→Writing) |
|
|
107
86
|
| `shikigami-purpose-discovery` | 目的探索を開始 |
|
|
108
87
|
| `shikigami-deep-research` | Deep Researchを実行 |
|
|
109
|
-
| `shikigami-framework-analysis` |
|
|
110
|
-
| `shikigami-report-writing` |
|
|
111
|
-
| `shikigami-full-research` | 統合リサーチを実行 |
|
|
112
|
-
|
|
113
|
-
## 📚 マッキンゼー・フレームワーク一覧
|
|
114
|
-
|
|
115
|
-
### 問題解決・仮説思考
|
|
116
|
-
| フレームワーク | 説明 |
|
|
117
|
-
|--------------|------|
|
|
118
|
-
| **仮説駆動アプローチ** | 仮説を立て、検証し、結論を導く問題解決手法 |
|
|
119
|
-
| **イシューツリー** | 問題をMECEに分解し、解決の優先順位を明確化 |
|
|
120
|
-
| **ピラミッド原則** | 結論先行で論理的にコミュニケーション(Barbara Minto) |
|
|
121
|
-
| **MECE** | 漏れなくダブりなく整理 |
|
|
122
|
-
|
|
123
|
-
### 組織・戦略
|
|
124
|
-
| フレームワーク | 説明 |
|
|
125
|
-
|--------------|------|
|
|
126
|
-
| **マッキンゼーの7S** | 組織の7要素(Strategy, Structure, Systems, Shared Values, Style, Staff, Skills)の整合性分析 |
|
|
127
|
-
| **3つの地平線** | 短期(H1)・中期(H2)・長期(H3)の成長を同時管理 |
|
|
128
|
-
| **成長のグラニュラリティ** | 成長を詳細に分解して真の成長源を特定 |
|
|
129
|
-
|
|
130
|
-
### 業績改善
|
|
131
|
-
| フレームワーク | 説明 |
|
|
132
|
-
|--------------|------|
|
|
133
|
-
| **間接費価値分析(OVA)** | 間接部門のコストと価値を分析し最適化 |
|
|
134
|
-
| **プロフィットプール分析** | バリューチェーン全体の利益分布を可視化 |
|
|
135
|
-
| **SCP分析** | 産業構造→企業行動→業績の因果関係を分析 |
|
|
136
|
-
|
|
137
|
-
## 🔄 マッキンゼー式ワークフロー
|
|
88
|
+
| `shikigami-framework-analysis` | フレームワーク分析を実行 |
|
|
89
|
+
| `shikigami-report-writing` | レポートを生成 |
|
|
138
90
|
|
|
139
|
-
|
|
140
|
-
flowchart TB
|
|
141
|
-
subgraph Phase1["Phase 1: 問題定義"]
|
|
142
|
-
P1A[クライアントの真の課題特定] --> P1B[成功の定義]
|
|
143
|
-
P1B --> P1C[スコープと制約の確認]
|
|
144
|
-
end
|
|
145
|
-
|
|
146
|
-
subgraph Phase2["Phase 2: 仮説構築"]
|
|
147
|
-
P2A[初期仮説の設定] --> P2B[支持論拠の整理]
|
|
148
|
-
P2B --> P2C[反証可能な形式で表現]
|
|
149
|
-
end
|
|
150
|
-
|
|
151
|
-
subgraph Phase3["Phase 3: イシュー分解"]
|
|
152
|
-
P3A[イシューツリー作成] --> P3B[優先順位付け]
|
|
153
|
-
P3B --> P3C[分析計画策定]
|
|
154
|
-
end
|
|
155
|
-
|
|
156
|
-
subgraph Phase4["Phase 4: データ収集・分析"]
|
|
157
|
-
P4A[定量・定性データ収集] --> P4B[80/20分析]
|
|
158
|
-
P4B --> P4C[So What?で洞察抽出]
|
|
159
|
-
end
|
|
160
|
-
|
|
161
|
-
subgraph Phase5["Phase 5: 統合・提言"]
|
|
162
|
-
P5A[ピラミッド原則で構造化] --> P5B[アクションプラン策定]
|
|
163
|
-
P5B --> P5C[インパクト評価]
|
|
164
|
-
end
|
|
165
|
-
|
|
166
|
-
Phase1 --> Phase2
|
|
167
|
-
Phase2 --> Phase3
|
|
168
|
-
Phase3 --> Phase4
|
|
169
|
-
Phase4 --> Phase5
|
|
170
|
-
```
|
|
91
|
+
### 分析・評価
|
|
171
92
|
|
|
172
|
-
|
|
93
|
+
| プロンプト | 説明 |
|
|
94
|
+
|-----------|------|
|
|
95
|
+
| `shikigami-swot-generation` | SWOT分析を生成 |
|
|
96
|
+
| `shikigami-matching-analysis` | マッチング分析(適合性評価) |
|
|
97
|
+
| `shikigami-financial-analysis` | 財務分析 |
|
|
98
|
+
| `shikigami-comparison-table` | 比較表の生成 |
|
|
99
|
+
| `shikigami-trend-visualization` | トレンド可視化 |
|
|
173
100
|
|
|
174
|
-
###
|
|
101
|
+
### 品質・検証
|
|
175
102
|
|
|
176
|
-
|
|
177
|
-
|
|
103
|
+
| プロンプト | 説明 |
|
|
104
|
+
|-----------|------|
|
|
105
|
+
| `shikigami-source-critique` | ソース信頼性の批評的評価 |
|
|
106
|
+
| `shikigami-risk-sensitivity` | リスク感度分析 |
|
|
107
|
+
| `shikigami-stakeholder-impact` | ステークホルダー影響分析 |
|
|
108
|
+
| `shikigami-consistency-check` | 整合性チェック |
|
|
109
|
+
| `shikigami-phase-gate` | フェーズゲート(品質関門)レビュー |
|
|
110
|
+
| `shikigami-citation-formatter` | 引用フォーマット整形 |
|
|
111
|
+
| `shikigami-executive-sync` | エグゼクティブ向け要約 |
|
|
178
112
|
|
|
179
|
-
|
|
180
|
-
1. [Planner] 「成長の目標は何ですか?」→ 真の目的を探索
|
|
181
|
-
2. [Deep Research] 市場情報を仮説駆動で収集(web_search / web_fetch 活用)
|
|
182
|
-
3. [Framework] 3つの地平線、成長のグラニュラリティを適用
|
|
183
|
-
4. [Writing] ピラミッド原則で構造化した提言レポートを生成
|
|
184
|
-
```
|
|
113
|
+
### 出力
|
|
185
114
|
|
|
186
|
-
|
|
115
|
+
| プロンプト | 説明 |
|
|
116
|
+
|-----------|------|
|
|
117
|
+
| `shikigami-qiita-article` | Qiita記事形式で出力 |
|
|
118
|
+
| `shikigami-manifest` | リサーチマニフェスト生成 |
|
|
187
119
|
|
|
188
|
-
|
|
189
|
-
ユーザー: 「組織の問題点を分析して」
|
|
120
|
+
## 📚 コンサルティング・フレームワーク一覧(50+)
|
|
190
121
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
2. [Deep Research] 組織情報を収集
|
|
194
|
-
3. [Framework] マッキンゼーの7Sで整合性を分析
|
|
195
|
-
4. [Writing] イシューツリーで課題を構造化したレポートを生成
|
|
196
|
-
```
|
|
122
|
+
### 戦略分析
|
|
123
|
+
SWOT、PEST、5Forces、3C、BCG Matrix、GE Matrix、Ansoff Matrix、Blue Ocean、VRIO、Value Chain
|
|
197
124
|
|
|
198
|
-
###
|
|
125
|
+
### マーケティング
|
|
126
|
+
4P、4C、STP、AIDMA/AISAS、Customer Journey、Persona、Positioning Map
|
|
199
127
|
|
|
200
|
-
|
|
201
|
-
|
|
128
|
+
### 問題解決
|
|
129
|
+
MECE、5 Whys、Issue Tree、Logic Tree、Fishbone、As-Is/To-Be
|
|
202
130
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
131
|
+
### 組織・プロセス
|
|
132
|
+
7S、RACI、PDCA、OODA、ECRS
|
|
133
|
+
|
|
134
|
+
### イノベーション
|
|
135
|
+
Business Model Canvas、Lean Canvas、Design Thinking、SCAMPER、TAM/SAM/SOM、AARRR、Value Proposition Canvas
|
|
136
|
+
|
|
137
|
+
### 意思決定
|
|
138
|
+
Decision Matrix、Cost-Benefit、Risk Matrix、Pros-Cons
|
|
139
|
+
|
|
140
|
+
### 思考フレームワーク
|
|
141
|
+
Pyramid Principle、So What/Why So、PREP
|
|
142
|
+
|
|
143
|
+
### PwC固有
|
|
144
|
+
BXT、Strategy&、Fit for Growth、Digital Maturity、ESG Integration、Three Lines of Defense、TIMM
|
|
209
145
|
|
|
210
146
|
## 🤝 Microsoft 365 Copilot Cowork 連携
|
|
211
147
|
|
|
@@ -226,6 +162,47 @@ npx shikigami cowork --out ./dist
|
|
|
226
162
|
|
|
227
163
|
**参照**: [Cowork Plugin Development](https://learn.microsoft.com/microsoft-365/copilot/cowork/cowork-plugin-development)
|
|
228
164
|
|
|
165
|
+
## 🚀 CLI コマンド
|
|
166
|
+
|
|
167
|
+
```bash
|
|
168
|
+
npx shikigami init # SHIKIGAMIファイルを初期化
|
|
169
|
+
npx shikigami upgrade # 最新版にアップグレード
|
|
170
|
+
npx shikigami cowork # Coworkプラグインをビルド
|
|
171
|
+
npx shikigami new [name] # 新規リサーチプロジェクト作成
|
|
172
|
+
npx shikigami find-related [keyword] # 関連プロジェクト検索
|
|
173
|
+
npx shikigami inherit [options] # 過去レポートからナレッジ継承
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
## 🔄 リサーチワークフロー
|
|
177
|
+
|
|
178
|
+
```mermaid
|
|
179
|
+
flowchart TB
|
|
180
|
+
subgraph Phase1["Phase 1: 目的探索"]
|
|
181
|
+
P1A[ユーザーの依頼] --> P1B[5 Whys / JTBD で真の目的を発見]
|
|
182
|
+
P1B --> P1C[リサーチ計画策定]
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
subgraph Phase2["Phase 2: Deep Research"]
|
|
186
|
+
P2A[Think: 仮説構築] --> P2B[Report: 情報収集・検証]
|
|
187
|
+
P2B --> P2C[Action: 次のリサーチ方向決定]
|
|
188
|
+
P2C --> P2A
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
subgraph Phase3["Phase 3: フレームワーク分析"]
|
|
192
|
+
P3A[適切なフレームワーク選定] --> P3B[50+フレームワークから分析実行]
|
|
193
|
+
P3B --> P3C[洞察の抽出・統合]
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
subgraph Phase4["Phase 4: レポート生成"]
|
|
197
|
+
P4A[ハルシネーション検証] --> P4B[引用・ソース管理]
|
|
198
|
+
P4B --> P4C[構造化レポート出力]
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
Phase1 --> Phase2
|
|
202
|
+
Phase2 --> Phase3
|
|
203
|
+
Phase3 --> Phase4
|
|
204
|
+
```
|
|
205
|
+
|
|
229
206
|
## 📄 ライセンス
|
|
230
207
|
|
|
231
208
|
MIT License
|
package/mcp-server/package.json
CHANGED
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* SHIKIGAMI Cowork Plugin Builder
|
|
4
4
|
*
|
|
5
5
|
* Builds a Microsoft 365 Copilot Cowork plugin package (.zip)
|
|
6
|
-
* from SHIKIGAMI's Agent Skills.
|
|
6
|
+
* from SHIKIGAMI's Agent Skills following the official "Build from scratch" guide.
|
|
7
7
|
*
|
|
8
8
|
* Usage: npx shikigami cowork [options]
|
|
9
9
|
* --out <dir> Output directory (default: ./dist)
|
|
@@ -11,11 +11,14 @@
|
|
|
11
11
|
* --help Show help
|
|
12
12
|
*
|
|
13
13
|
* Reference: https://learn.microsoft.com/microsoft-365/copilot/cowork/cowork-plugin-development
|
|
14
|
+
*
|
|
15
|
+
* Cross-platform: The same SKILL.md files work in GitHub Copilot CLI,
|
|
16
|
+
* VS Code Copilot, Claude Code, Gemini CLI, and Copilot Cowork.
|
|
14
17
|
*/
|
|
15
18
|
|
|
16
19
|
const fs = require('fs');
|
|
17
20
|
const path = require('path');
|
|
18
|
-
const {
|
|
21
|
+
const { execFileSync } = require('child_process');
|
|
19
22
|
|
|
20
23
|
// ── Constants ──────────────────────────────────────────────────────────────
|
|
21
24
|
|
|
@@ -23,16 +26,32 @@ const MAX_COMPANIONS_PER_SKILL = 20;
|
|
|
23
26
|
const MAX_COMPANION_SIZE = 5 * 1024 * 1024; // 5 MB
|
|
24
27
|
const MAX_COMPANION_TOTAL = 10 * 1024 * 1024; // 10 MB per skill
|
|
25
28
|
const MAX_SKILLS = 20;
|
|
29
|
+
const MAX_FOLDER_PATH = 256;
|
|
30
|
+
const RECOMMENDED_BODY_WORDS = 3000;
|
|
26
31
|
|
|
27
32
|
const PACKAGE_NAME = 'com.nahisaho.shikigami';
|
|
28
|
-
const APP_ID = 'a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d';
|
|
33
|
+
const APP_ID = 'a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d';
|
|
29
34
|
const ACCENT_COLOR = '#6B46C1';
|
|
30
35
|
|
|
31
|
-
|
|
36
|
+
const WINDOWS_RESERVED = new Set([
|
|
37
|
+
'CON', 'PRN', 'AUX', 'NUL',
|
|
38
|
+
'COM1', 'COM2', 'COM3', 'COM4', 'COM5', 'COM6', 'COM7', 'COM8', 'COM9',
|
|
39
|
+
'LPT1', 'LPT2', 'LPT3', 'LPT4', 'LPT5', 'LPT6', 'LPT7', 'LPT8', 'LPT9',
|
|
40
|
+
]);
|
|
41
|
+
|
|
42
|
+
const SAFE_FILENAME_RE = /^[a-zA-Z0-9\-_. !]+$/;
|
|
43
|
+
|
|
44
|
+
// Cowork-specific frontmatter defaults per skill
|
|
45
|
+
const COWORK_DEFAULTS = {
|
|
46
|
+
'shikigami-planner': { category: 'Research', icon: 'Lightbulb' },
|
|
47
|
+
'shikigami-deep-research': { category: 'Research', icon: 'Search' },
|
|
48
|
+
'shikigami-consulting-framework': { category: 'Analysis', icon: 'Briefcase' },
|
|
49
|
+
'shikigami-writing': { category: 'Writing', icon: 'DocumentBulletList' },
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
// ── Minimal PNG generator (no dependencies) ───────────────────────────────
|
|
32
53
|
|
|
33
54
|
function createMinimalPNG(width, height, r, g, b) {
|
|
34
|
-
// Creates a minimal valid PNG with a solid color
|
|
35
|
-
// PNG signature
|
|
36
55
|
const signature = Buffer.from([137, 80, 78, 71, 13, 10, 26, 10]);
|
|
37
56
|
|
|
38
57
|
function crc32(buf) {
|
|
@@ -53,26 +72,20 @@ function createMinimalPNG(width, height, r, g, b) {
|
|
|
53
72
|
const len = Buffer.alloc(4);
|
|
54
73
|
len.writeUInt32BE(data.length);
|
|
55
74
|
const typeAndData = Buffer.concat([Buffer.from(type), data]);
|
|
56
|
-
const
|
|
57
|
-
|
|
58
|
-
return Buffer.concat([len, typeAndData,
|
|
75
|
+
const crcBuf = Buffer.alloc(4);
|
|
76
|
+
crcBuf.writeUInt32BE(crc32(typeAndData));
|
|
77
|
+
return Buffer.concat([len, typeAndData, crcBuf]);
|
|
59
78
|
}
|
|
60
79
|
|
|
61
|
-
// IHDR
|
|
62
80
|
const ihdr = Buffer.alloc(13);
|
|
63
81
|
ihdr.writeUInt32BE(width, 0);
|
|
64
82
|
ihdr.writeUInt32BE(height, 4);
|
|
65
|
-
ihdr[8] = 8;
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
ihdr[11] = 0; // filter
|
|
69
|
-
ihdr[12] = 0; // interlace
|
|
70
|
-
|
|
71
|
-
// IDAT - raw image data
|
|
72
|
-
const rowSize = 1 + width * 3; // filter byte + RGB per pixel
|
|
83
|
+
ihdr[8] = 8; ihdr[9] = 2; ihdr[10] = 0; ihdr[11] = 0; ihdr[12] = 0;
|
|
84
|
+
|
|
85
|
+
const rowSize = 1 + width * 3;
|
|
73
86
|
const rawData = Buffer.alloc(rowSize * height);
|
|
74
87
|
for (let y = 0; y < height; y++) {
|
|
75
|
-
rawData[y * rowSize] = 0;
|
|
88
|
+
rawData[y * rowSize] = 0;
|
|
76
89
|
for (let x = 0; x < width; x++) {
|
|
77
90
|
const offset = y * rowSize + 1 + x * 3;
|
|
78
91
|
rawData[offset] = r;
|
|
@@ -84,18 +97,15 @@ function createMinimalPNG(width, height, r, g, b) {
|
|
|
84
97
|
const zlib = require('zlib');
|
|
85
98
|
const compressed = zlib.deflateSync(rawData);
|
|
86
99
|
|
|
87
|
-
// IEND
|
|
88
|
-
const iend = Buffer.alloc(0);
|
|
89
|
-
|
|
90
100
|
return Buffer.concat([
|
|
91
101
|
signature,
|
|
92
102
|
makeChunk('IHDR', ihdr),
|
|
93
103
|
makeChunk('IDAT', compressed),
|
|
94
|
-
makeChunk('IEND',
|
|
104
|
+
makeChunk('IEND', Buffer.alloc(0)),
|
|
95
105
|
]);
|
|
96
106
|
}
|
|
97
107
|
|
|
98
|
-
// ──
|
|
108
|
+
// ── YAML Frontmatter Parser ───────────────────────────────────────────────
|
|
99
109
|
|
|
100
110
|
function parseFrontmatter(content) {
|
|
101
111
|
const match = content.match(/^---\n([\s\S]*?)\n---/);
|
|
@@ -103,58 +113,123 @@ function parseFrontmatter(content) {
|
|
|
103
113
|
|
|
104
114
|
const yaml = match[1];
|
|
105
115
|
const result = {};
|
|
106
|
-
|
|
107
|
-
// Simple YAML parser for frontmatter fields
|
|
108
116
|
let currentKey = null;
|
|
109
|
-
let
|
|
117
|
+
let blockLines = [];
|
|
110
118
|
let inBlock = false;
|
|
119
|
+
let currentIndent = 0;
|
|
120
|
+
let mapKey = null;
|
|
121
|
+
let mapObj = null;
|
|
122
|
+
|
|
123
|
+
function flushBlock() {
|
|
124
|
+
if (inBlock && currentKey) {
|
|
125
|
+
result[currentKey] = blockLines.join('\n').trim();
|
|
126
|
+
inBlock = false;
|
|
127
|
+
blockLines = [];
|
|
128
|
+
}
|
|
129
|
+
if (mapKey && mapObj) {
|
|
130
|
+
result[mapKey] = mapObj;
|
|
131
|
+
mapKey = null;
|
|
132
|
+
mapObj = null;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
111
135
|
|
|
112
136
|
for (const line of yaml.split('\n')) {
|
|
137
|
+
// Handle block scalar continuation
|
|
113
138
|
if (inBlock) {
|
|
114
139
|
if (line.match(/^\s{2,}/) || line.trim() === '') {
|
|
115
|
-
|
|
140
|
+
blockLines.push(line.replace(/^\s{2}/, ''));
|
|
116
141
|
continue;
|
|
117
142
|
} else {
|
|
118
|
-
|
|
119
|
-
|
|
143
|
+
flushBlock();
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Handle map continuation (metadata:, cowork: style)
|
|
148
|
+
if (mapKey && line.match(/^\s{2,}\w/)) {
|
|
149
|
+
const kvMatch = line.trim().match(/^(\w[\w.-]*)\s*:\s*(.*)/);
|
|
150
|
+
if (kvMatch) {
|
|
151
|
+
mapObj[kvMatch[1]] = kvMatch[2].trim().replace(/^["']|["']$/g, '');
|
|
152
|
+
continue;
|
|
120
153
|
}
|
|
154
|
+
} else if (mapKey) {
|
|
155
|
+
flushBlock();
|
|
121
156
|
}
|
|
122
157
|
|
|
158
|
+
// Top-level key: value
|
|
123
159
|
const kvMatch = line.match(/^(\w[\w.-]*)\s*:\s*(.*)/);
|
|
124
160
|
if (kvMatch) {
|
|
125
161
|
currentKey = kvMatch[1];
|
|
126
162
|
const value = kvMatch[2].trim();
|
|
127
163
|
if (value === '|' || value === '>') {
|
|
128
164
|
inBlock = true;
|
|
129
|
-
|
|
165
|
+
blockLines = [];
|
|
166
|
+
} else if (value === '') {
|
|
167
|
+
// Could be a map start (metadata:, cowork:)
|
|
168
|
+
mapKey = currentKey;
|
|
169
|
+
mapObj = {};
|
|
130
170
|
} else {
|
|
131
171
|
result[currentKey] = value.replace(/^["']|["']$/g, '');
|
|
132
172
|
}
|
|
133
173
|
}
|
|
134
174
|
}
|
|
135
175
|
|
|
136
|
-
|
|
137
|
-
|
|
176
|
+
flushBlock();
|
|
177
|
+
return result;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function extractBody(content) {
|
|
181
|
+
const match = content.match(/^---\n[\s\S]*?\n---\n([\s\S]*)/);
|
|
182
|
+
return match ? match[1].trim() : content;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// ── Companion File Path Validation ────────────────────────────────────────
|
|
186
|
+
|
|
187
|
+
function validateCompanionPath(relPath) {
|
|
188
|
+
const errors = [];
|
|
189
|
+
const parts = relPath.split('/');
|
|
190
|
+
|
|
191
|
+
// No absolute paths
|
|
192
|
+
if (path.isAbsolute(relPath)) {
|
|
193
|
+
errors.push(`Absolute path not allowed: ${relPath}`);
|
|
194
|
+
}
|
|
195
|
+
// No path traversal
|
|
196
|
+
if (parts.includes('..')) {
|
|
197
|
+
errors.push(`Path traversal (..) not allowed: ${relPath}`);
|
|
198
|
+
}
|
|
199
|
+
// No backslashes or null bytes
|
|
200
|
+
if (relPath.includes('\\') || relPath.includes('\0')) {
|
|
201
|
+
errors.push(`Backslash or null byte in path: ${relPath}`);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
for (const part of parts) {
|
|
205
|
+
// No hidden files
|
|
206
|
+
if (part.startsWith('.')) {
|
|
207
|
+
errors.push(`Hidden file not allowed: ${relPath}`);
|
|
208
|
+
}
|
|
209
|
+
// No Windows reserved names
|
|
210
|
+
const baseName = part.replace(/\.[^.]*$/, '').toUpperCase();
|
|
211
|
+
if (WINDOWS_RESERVED.has(baseName)) {
|
|
212
|
+
errors.push(`Windows reserved name: ${part} in ${relPath}`);
|
|
213
|
+
}
|
|
214
|
+
// Safe characters only
|
|
215
|
+
if (!SAFE_FILENAME_RE.test(part)) {
|
|
216
|
+
errors.push(`Unsafe characters in filename: ${part} in ${relPath}`);
|
|
217
|
+
}
|
|
138
218
|
}
|
|
139
219
|
|
|
140
|
-
return
|
|
220
|
+
return errors;
|
|
141
221
|
}
|
|
142
222
|
|
|
143
|
-
// ── Companion File Consolidation
|
|
223
|
+
// ── Companion File Consolidation ──────────────────────────────────────────
|
|
144
224
|
|
|
145
|
-
function
|
|
146
|
-
/**
|
|
147
|
-
* If a skill has more than MAX_COMPANIONS_PER_SKILL companion files,
|
|
148
|
-
* consolidate them into category-level reference files.
|
|
149
|
-
* Returns an array of { relativePath, content } for the consolidated files.
|
|
150
|
-
*/
|
|
151
|
-
const files = [];
|
|
225
|
+
function discoverCompanions(skillDir) {
|
|
152
226
|
const allFiles = [];
|
|
153
227
|
|
|
154
228
|
function walkDir(dir, prefix) {
|
|
155
229
|
if (!fs.existsSync(dir)) return;
|
|
156
230
|
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
157
231
|
for (const entry of entries) {
|
|
232
|
+
if (entry.name.startsWith('.')) continue; // skip hidden
|
|
158
233
|
const fullPath = path.join(dir, entry.name);
|
|
159
234
|
const relPath = prefix ? `${prefix}/${entry.name}` : entry.name;
|
|
160
235
|
if (entry.isDirectory()) {
|
|
@@ -166,46 +241,63 @@ function consolidateCompanions(skillDir, skillName) {
|
|
|
166
241
|
}
|
|
167
242
|
|
|
168
243
|
walkDir(skillDir, '');
|
|
244
|
+
return allFiles;
|
|
245
|
+
}
|
|
169
246
|
|
|
247
|
+
function consolidateCompanions(allFiles, skillName) {
|
|
170
248
|
if (allFiles.length <= MAX_COMPANIONS_PER_SKILL) {
|
|
171
|
-
// No consolidation needed - return original files
|
|
172
249
|
return allFiles.map(f => ({
|
|
173
250
|
relativePath: f.relPath,
|
|
174
251
|
content: fs.readFileSync(f.fullPath),
|
|
175
252
|
}));
|
|
176
253
|
}
|
|
177
254
|
|
|
255
|
+
const files = [];
|
|
256
|
+
|
|
178
257
|
// Group by top-level directory
|
|
179
|
-
const
|
|
258
|
+
const topGroups = {};
|
|
180
259
|
for (const f of allFiles) {
|
|
181
|
-
const
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
if (!groups[subDir]) groups[subDir] = [];
|
|
185
|
-
groups[subDir].push(f);
|
|
260
|
+
const topDir = f.relPath.split('/')[0] || '_root';
|
|
261
|
+
if (!topGroups[topDir]) topGroups[topDir] = [];
|
|
262
|
+
topGroups[topDir].push(f);
|
|
186
263
|
}
|
|
187
264
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
265
|
+
for (const [dir, dirFiles] of Object.entries(topGroups)) {
|
|
266
|
+
if (dir === '_root') {
|
|
267
|
+
for (const f of dirFiles) {
|
|
268
|
+
files.push({
|
|
269
|
+
relativePath: f.relPath,
|
|
270
|
+
content: fs.readFileSync(f.fullPath),
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
continue;
|
|
195
274
|
}
|
|
196
275
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
276
|
+
// Check if sub-directory grouping keeps within limits
|
|
277
|
+
const subGroups = {};
|
|
278
|
+
for (const f of dirFiles) {
|
|
279
|
+
const parts = f.relPath.split('/');
|
|
280
|
+
const subKey = parts.length > 1 ? parts[1] : '_direct';
|
|
281
|
+
if (!subGroups[subKey]) subGroups[subKey] = [];
|
|
282
|
+
subGroups[subKey].push(f);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// If sub-grouping fits, use it; otherwise consolidate entire directory
|
|
286
|
+
if (Object.keys(subGroups).length <= MAX_COMPANIONS_PER_SKILL - files.length) {
|
|
287
|
+
for (const [sub, subFiles] of Object.entries(subGroups)) {
|
|
288
|
+
let consolidated = `# ${dir}/${sub}\n\nConsolidated reference for ${skillName}.\n\n`;
|
|
289
|
+
for (const f of subFiles) {
|
|
290
|
+
const content = fs.readFileSync(f.fullPath, 'utf-8');
|
|
291
|
+
const basename = path.basename(f.name, path.extname(f.name));
|
|
292
|
+
consolidated += `---\n\n## ${basename}\n\n${content}\n\n`;
|
|
204
293
|
}
|
|
205
|
-
|
|
294
|
+
files.push({
|
|
295
|
+
relativePath: `references/${dir}-${sub}.md`,
|
|
296
|
+
content: Buffer.from(consolidated, 'utf-8'),
|
|
297
|
+
});
|
|
206
298
|
}
|
|
207
|
-
|
|
208
|
-
// Consolidate
|
|
299
|
+
} else {
|
|
300
|
+
// Consolidate entire top-level directory into one file
|
|
209
301
|
let consolidated = `# ${dir}\n\nConsolidated reference for ${skillName}.\n\n`;
|
|
210
302
|
for (const f of dirFiles) {
|
|
211
303
|
const content = fs.readFileSync(f.fullPath, 'utf-8');
|
|
@@ -213,52 +305,119 @@ function consolidateCompanions(skillDir, skillName) {
|
|
|
213
305
|
consolidated += `---\n\n## ${basename}\n\n${content}\n\n`;
|
|
214
306
|
}
|
|
215
307
|
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
308
|
+
const consolidatedBuf = Buffer.from(consolidated, 'utf-8');
|
|
309
|
+
|
|
310
|
+
// Chunk if exceeds 5MB
|
|
311
|
+
if (consolidatedBuf.length > MAX_COMPANION_SIZE) {
|
|
312
|
+
const lines = consolidated.split('\n');
|
|
313
|
+
let chunk = [];
|
|
314
|
+
let chunkSize = 0;
|
|
315
|
+
let chunkIdx = 1;
|
|
316
|
+
for (const line of lines) {
|
|
317
|
+
const lineSize = Buffer.byteLength(line + '\n', 'utf-8');
|
|
318
|
+
if (chunkSize + lineSize > MAX_COMPANION_SIZE * 0.9 && chunk.length > 0) {
|
|
319
|
+
files.push({
|
|
320
|
+
relativePath: `references/${dir}-${String(chunkIdx).padStart(2, '0')}.md`,
|
|
321
|
+
content: Buffer.from(chunk.join('\n'), 'utf-8'),
|
|
322
|
+
});
|
|
323
|
+
chunk = [];
|
|
324
|
+
chunkSize = 0;
|
|
325
|
+
chunkIdx++;
|
|
326
|
+
}
|
|
327
|
+
chunk.push(line);
|
|
328
|
+
chunkSize += lineSize;
|
|
329
|
+
}
|
|
330
|
+
if (chunk.length > 0) {
|
|
226
331
|
files.push({
|
|
227
|
-
relativePath:
|
|
228
|
-
content:
|
|
332
|
+
relativePath: `references/${dir}-${String(chunkIdx).padStart(2, '0')}.md`,
|
|
333
|
+
content: Buffer.from(chunk.join('\n'), 'utf-8'),
|
|
229
334
|
});
|
|
230
335
|
}
|
|
231
|
-
|
|
336
|
+
} else {
|
|
337
|
+
files.push({
|
|
338
|
+
relativePath: `references/${dir}.md`,
|
|
339
|
+
content: consolidatedBuf,
|
|
340
|
+
});
|
|
232
341
|
}
|
|
342
|
+
}
|
|
343
|
+
}
|
|
233
344
|
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
345
|
+
return files;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
// ── SKILL.md Rewriter for Cowork ──────────────────────────────────────────
|
|
349
|
+
|
|
350
|
+
function rewriteSkillMdForCowork(content, originalCompanions, packagedCompanions, skillName) {
|
|
351
|
+
/**
|
|
352
|
+
* Creates a Cowork-compatible version of SKILL.md:
|
|
353
|
+
* 1. Adds cowork.category and cowork.icon to frontmatter if missing
|
|
354
|
+
* 2. Adds metadata.author and metadata.version if missing
|
|
355
|
+
* 3. Updates file path references to match consolidated paths
|
|
356
|
+
* 4. Adds "Additional Resources" section listing companion files
|
|
357
|
+
*/
|
|
358
|
+
let result = content;
|
|
359
|
+
|
|
360
|
+
// Update frontmatter with Cowork fields
|
|
361
|
+
const fmMatch = result.match(/^(---\n)([\s\S]*?)(\n---)/);
|
|
362
|
+
if (fmMatch) {
|
|
363
|
+
let fm = fmMatch[2];
|
|
364
|
+
const defaults = COWORK_DEFAULTS[skillName] || {};
|
|
365
|
+
|
|
366
|
+
if (!fm.includes('cowork.category') && defaults.category) {
|
|
367
|
+
fm += `\ncowork.category: ${defaults.category}`;
|
|
368
|
+
}
|
|
369
|
+
if (!fm.includes('cowork.icon') && defaults.icon) {
|
|
370
|
+
fm += `\ncowork.icon: ${defaults.icon}`;
|
|
371
|
+
}
|
|
372
|
+
if (!fm.includes('metadata:')) {
|
|
373
|
+
fm += `\nmetadata:\n author: nahisaho\n version: "2.0"`;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
result = `${fmMatch[1]}${fm}${fmMatch[3]}${result.slice(fmMatch[0].length)}`;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
// Build path mapping: original -> consolidated
|
|
380
|
+
if (originalCompanions.length > MAX_COMPANIONS_PER_SKILL) {
|
|
381
|
+
const pathMap = {};
|
|
382
|
+
for (const orig of originalCompanions) {
|
|
383
|
+
const topDir = orig.relPath.split('/')[0];
|
|
384
|
+
const consolidated = packagedCompanions.find(
|
|
385
|
+
p => p.relativePath.startsWith(`references/${topDir}`)
|
|
386
|
+
);
|
|
387
|
+
if (consolidated) {
|
|
388
|
+
pathMap[orig.relPath] = consolidated.relativePath;
|
|
239
389
|
}
|
|
390
|
+
}
|
|
240
391
|
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
});
|
|
392
|
+
// Replace file path references in body
|
|
393
|
+
for (const [origPath, newPath] of Object.entries(pathMap)) {
|
|
394
|
+
result = result.replace(new RegExp(escapeRegex(origPath), 'g'), newPath);
|
|
245
395
|
}
|
|
246
396
|
}
|
|
247
397
|
|
|
248
|
-
|
|
398
|
+
// Add Additional Resources section if companions exist and not already present
|
|
399
|
+
if (packagedCompanions.length > 0 && !result.includes('## Additional Resources')) {
|
|
400
|
+
let resources = '\n\n## Additional Resources\n\n';
|
|
401
|
+
for (const c of packagedCompanions) {
|
|
402
|
+
const name = path.basename(c.relativePath, path.extname(c.relativePath));
|
|
403
|
+
resources += `- **\`${c.relativePath}\`** — ${name}\n`;
|
|
404
|
+
}
|
|
405
|
+
result += resources;
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
return result;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
function escapeRegex(str) {
|
|
412
|
+
return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
249
413
|
}
|
|
250
414
|
|
|
251
|
-
// ── ZIP Builder
|
|
415
|
+
// ── ZIP Builder ───────────────────────────────────────────────────────────
|
|
252
416
|
|
|
253
417
|
function createZip(outputPath, files) {
|
|
254
|
-
/**
|
|
255
|
-
* Creates a zip file from an array of { path, content } entries.
|
|
256
|
-
* Uses a temp directory and system zip command.
|
|
257
|
-
*/
|
|
258
418
|
const tmpDir = fs.mkdtempSync(path.join(require('os').tmpdir(), 'shikigami-cowork-'));
|
|
259
419
|
|
|
260
420
|
try {
|
|
261
|
-
// Write all files to temp directory
|
|
262
421
|
for (const file of files) {
|
|
263
422
|
const filePath = path.join(tmpDir, file.path);
|
|
264
423
|
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
@@ -269,19 +428,18 @@ function createZip(outputPath, files) {
|
|
|
269
428
|
}
|
|
270
429
|
}
|
|
271
430
|
|
|
272
|
-
// Create zip
|
|
273
431
|
const absOutput = path.resolve(outputPath);
|
|
274
432
|
try {
|
|
275
|
-
|
|
433
|
+
execFileSync('zip', ['-r', absOutput, '.'], { cwd: tmpDir, stdio: 'pipe' });
|
|
276
434
|
} catch {
|
|
277
|
-
// Fallback:
|
|
435
|
+
// Fallback: PowerShell (Windows)
|
|
278
436
|
try {
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
{
|
|
282
|
-
);
|
|
437
|
+
execFileSync('powershell', [
|
|
438
|
+
'-Command',
|
|
439
|
+
`Compress-Archive -Path '${tmpDir}${path.sep}*' -DestinationPath '${absOutput}' -Force`
|
|
440
|
+
], { stdio: 'pipe' });
|
|
283
441
|
} catch {
|
|
284
|
-
console.error('❌ zip command not found. Install zip or use PowerShell.');
|
|
442
|
+
console.error('❌ zip command not found. Install zip or use PowerShell on Windows.');
|
|
285
443
|
process.exit(1);
|
|
286
444
|
}
|
|
287
445
|
}
|
|
@@ -307,14 +465,15 @@ function generateManifest(skills, version) {
|
|
|
307
465
|
},
|
|
308
466
|
name: {
|
|
309
467
|
short: 'SHIKIGAMI',
|
|
310
|
-
full: 'SHIKIGAMI - Deep Research & Consulting Agent Skills
|
|
468
|
+
full: 'SHIKIGAMI - Deep Research & Consulting Agent Skills',
|
|
311
469
|
},
|
|
312
470
|
description: {
|
|
313
471
|
short: 'AI-powered deep research and consulting framework analysis',
|
|
314
|
-
full: 'SHIKIGAMI provides 4 specialized Agent Skills
|
|
472
|
+
full: 'SHIKIGAMI provides 4 specialized Agent Skills: '
|
|
315
473
|
+ 'deep research with hallucination prevention, 50+ consulting frameworks '
|
|
316
474
|
+ '(SWOT, PEST, 5Forces, BCG Matrix, etc.), structured report writing, '
|
|
317
|
-
+ 'and project planning with 5 Whys/JTBD purpose discovery.'
|
|
475
|
+
+ 'and project planning with 5 Whys/JTBD purpose discovery. '
|
|
476
|
+
+ 'Cross-platform compatible with GitHub Copilot CLI, VS Code, Claude Code, and Gemini CLI.',
|
|
318
477
|
},
|
|
319
478
|
icons: {
|
|
320
479
|
color: 'color.png',
|
|
@@ -325,7 +484,44 @@ function generateManifest(skills, version) {
|
|
|
325
484
|
};
|
|
326
485
|
}
|
|
327
486
|
|
|
328
|
-
// ── Validation
|
|
487
|
+
// ── Validation (ASKILL codes from official docs) ──────────────────────────
|
|
488
|
+
|
|
489
|
+
function validateManifest(manifest) {
|
|
490
|
+
const errors = [];
|
|
491
|
+
|
|
492
|
+
// ASKILL-M001: folder required in each agentSkills entry
|
|
493
|
+
for (const skill of manifest.agentSkills || []) {
|
|
494
|
+
if (!skill.folder) {
|
|
495
|
+
errors.push('[ASKILL-M001] `folder` is required for each agentSkills entry');
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
// ASKILL-M002: max 20 skills
|
|
500
|
+
if ((manifest.agentSkills || []).length > MAX_SKILLS) {
|
|
501
|
+
errors.push(`[ASKILL-M002] Max ${MAX_SKILLS} agentSkills entries (found ${manifest.agentSkills.length})`);
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
// ASKILL-M003: folder path max 256 chars
|
|
505
|
+
for (const skill of manifest.agentSkills || []) {
|
|
506
|
+
if (skill.folder && skill.folder.length > MAX_FOLDER_PATH) {
|
|
507
|
+
errors.push(`[ASKILL-M003] Folder path exceeds ${MAX_FOLDER_PATH} chars: ${skill.folder}`);
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
// ASKILL-P008: no duplicate folder values
|
|
512
|
+
const folders = (manifest.agentSkills || []).map(s => s.folder);
|
|
513
|
+
const dupes = folders.filter((f, i) => folders.indexOf(f) !== i);
|
|
514
|
+
if (dupes.length > 0) {
|
|
515
|
+
errors.push(`[ASKILL-P008] Duplicate folder values: ${[...new Set(dupes)].join(', ')}`);
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
// GUID format
|
|
519
|
+
if (!manifest.id || !/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(manifest.id)) {
|
|
520
|
+
errors.push('[Manifest] Invalid GUID format for id');
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
return errors;
|
|
524
|
+
}
|
|
329
525
|
|
|
330
526
|
function validateSkill(skillDir, skillName) {
|
|
331
527
|
const errors = [];
|
|
@@ -333,57 +529,87 @@ function validateSkill(skillDir, skillName) {
|
|
|
333
529
|
|
|
334
530
|
const skillMdPath = path.join(skillDir, 'SKILL.md');
|
|
335
531
|
if (!fs.existsSync(skillMdPath)) {
|
|
336
|
-
|
|
532
|
+
// ASKILL-P002
|
|
533
|
+
errors.push(`[ASKILL-P002] SKILL.md not found in ${skillName}`);
|
|
337
534
|
return { errors, warnings };
|
|
338
535
|
}
|
|
339
536
|
|
|
340
537
|
const content = fs.readFileSync(skillMdPath, 'utf-8');
|
|
341
538
|
const frontmatter = parseFrontmatter(content);
|
|
342
539
|
|
|
540
|
+
// ASKILL-P003: valid YAML frontmatter
|
|
343
541
|
if (!frontmatter) {
|
|
344
|
-
errors.push(
|
|
542
|
+
errors.push(`[ASKILL-P003] No valid YAML frontmatter in ${skillName}/SKILL.md`);
|
|
345
543
|
return { errors, warnings };
|
|
346
544
|
}
|
|
347
545
|
|
|
348
|
-
//
|
|
546
|
+
// ASKILL-P004: name field required
|
|
349
547
|
if (!frontmatter.name) {
|
|
350
|
-
errors.push(
|
|
548
|
+
errors.push(`[ASKILL-P004] Missing 'name' field in ${skillName}/SKILL.md frontmatter`);
|
|
351
549
|
} else {
|
|
550
|
+
// ASKILL-P006: name matches folder name
|
|
352
551
|
if (frontmatter.name !== skillName) {
|
|
353
|
-
errors.push(
|
|
552
|
+
errors.push(`[ASKILL-P006] Frontmatter name '${frontmatter.name}' doesn't match folder '${skillName}'`);
|
|
354
553
|
}
|
|
554
|
+
// ASKILL-P007: name is kebab-case, 1-64 chars
|
|
355
555
|
if (frontmatter.name.length > 64) {
|
|
356
|
-
errors.push(
|
|
556
|
+
errors.push(`[ASKILL-P007] Name exceeds 64 characters: ${frontmatter.name}`);
|
|
357
557
|
}
|
|
358
558
|
if (!/^[a-z0-9]+(-[a-z0-9]+)*$/.test(frontmatter.name)) {
|
|
359
|
-
errors.push(
|
|
559
|
+
errors.push(`[ASKILL-P007] Name must be kebab-case: ${frontmatter.name}`);
|
|
360
560
|
}
|
|
361
561
|
}
|
|
362
562
|
|
|
363
|
-
//
|
|
563
|
+
// ASKILL-P005: description field required, 1-1024 chars
|
|
364
564
|
if (!frontmatter.description) {
|
|
365
|
-
errors.push(
|
|
565
|
+
errors.push(`[ASKILL-P005] Missing 'description' field in ${skillName}/SKILL.md frontmatter`);
|
|
366
566
|
} else if (frontmatter.description.length > 1024) {
|
|
367
|
-
|
|
567
|
+
errors.push(`[ASKILL-P005] Description exceeds 1024 characters (${frontmatter.description.length}) in ${skillName}`);
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
// Body size warning
|
|
571
|
+
const body = extractBody(content);
|
|
572
|
+
const wordCount = body.split(/\s+/).filter(w => w.length > 0).length;
|
|
573
|
+
if (wordCount > RECOMMENDED_BODY_WORDS) {
|
|
574
|
+
warnings.push(`${skillName}: SKILL.md body is ~${wordCount} words (recommended <${RECOMMENDED_BODY_WORDS}). Consider moving details to references/`);
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
// Discover and validate companion files
|
|
578
|
+
const originalCompanions = discoverCompanions(skillDir);
|
|
579
|
+
|
|
580
|
+
// Validate companion file paths
|
|
581
|
+
for (const f of originalCompanions) {
|
|
582
|
+
const pathErrors = validateCompanionPath(f.relPath);
|
|
583
|
+
for (const e of pathErrors) {
|
|
584
|
+
errors.push(`[Companion] ${skillName}: ${e}`);
|
|
585
|
+
}
|
|
368
586
|
}
|
|
369
587
|
|
|
370
|
-
//
|
|
371
|
-
const
|
|
372
|
-
|
|
373
|
-
|
|
588
|
+
// Consolidate companions for packaging
|
|
589
|
+
const packagedCompanions = consolidateCompanions(originalCompanions, skillName);
|
|
590
|
+
|
|
591
|
+
// Validate consolidated results
|
|
592
|
+
if (packagedCompanions.length > MAX_COMPANIONS_PER_SKILL) {
|
|
593
|
+
errors.push(`[Companion] ${skillName}: ${packagedCompanions.length} files after consolidation (max ${MAX_COMPANIONS_PER_SKILL})`);
|
|
374
594
|
}
|
|
375
595
|
|
|
376
|
-
const totalSize =
|
|
596
|
+
const totalSize = packagedCompanions.reduce((sum, f) => sum + f.content.length, 0);
|
|
377
597
|
if (totalSize > MAX_COMPANION_TOTAL) {
|
|
378
|
-
errors.push(
|
|
598
|
+
errors.push(`[Companion] ${skillName}: Total size ${(totalSize / 1024 / 1024).toFixed(1)}MB exceeds 10MB limit`);
|
|
379
599
|
}
|
|
380
600
|
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
601
|
+
for (const f of packagedCompanions) {
|
|
602
|
+
if (f.content.length > MAX_COMPANION_SIZE) {
|
|
603
|
+
errors.push(`[Companion] ${skillName}: ${f.relativePath} exceeds 5MB limit`);
|
|
604
|
+
}
|
|
605
|
+
// Validate packaged paths too
|
|
606
|
+
const pathErrors = validateCompanionPath(f.relativePath);
|
|
607
|
+
for (const e of pathErrors) {
|
|
608
|
+
errors.push(`[Companion] ${skillName}: ${e}`);
|
|
609
|
+
}
|
|
384
610
|
}
|
|
385
611
|
|
|
386
|
-
return { errors, warnings,
|
|
612
|
+
return { errors, warnings, originalCompanions, packagedCompanions, frontmatter, content };
|
|
387
613
|
}
|
|
388
614
|
|
|
389
615
|
// ── Main ───────────────────────────────────────────────────────────────────
|
|
@@ -402,6 +628,7 @@ function showHelp() {
|
|
|
402
628
|
🎭 SHIKIGAMI Cowork Plugin Builder
|
|
403
629
|
|
|
404
630
|
Builds a Microsoft 365 Copilot Cowork plugin package (.zip)
|
|
631
|
+
following the official "Build a plugin from scratch" guide.
|
|
405
632
|
|
|
406
633
|
Usage: npx shikigami cowork [options]
|
|
407
634
|
|
|
@@ -410,8 +637,17 @@ Options:
|
|
|
410
637
|
--check Validation only (dry-run)
|
|
411
638
|
-h, --help Show this help
|
|
412
639
|
|
|
413
|
-
|
|
414
|
-
|
|
640
|
+
Steps (per official docs):
|
|
641
|
+
1. Discover & validate SKILL.md files (frontmatter, naming, paths)
|
|
642
|
+
2. Consolidate companion files (max 20 per skill)
|
|
643
|
+
3. Rewrite SKILL.md for Cowork (add cowork.category, cowork.icon, metadata)
|
|
644
|
+
4. Generate manifest.json (M365 Unified App Manifest devPreview)
|
|
645
|
+
5. Generate icons (color.png 192×192, outline.png 32×32)
|
|
646
|
+
6. Create .zip package
|
|
647
|
+
|
|
648
|
+
Cross-platform:
|
|
649
|
+
Same SKILL.md files work in GitHub Copilot CLI, VS Code Copilot,
|
|
650
|
+
Claude Code, Gemini CLI, Cursor, JetBrains Junie, and Copilot Cowork.
|
|
415
651
|
|
|
416
652
|
Reference:
|
|
417
653
|
https://learn.microsoft.com/microsoft-365/copilot/cowork/cowork-plugin-development
|
|
@@ -431,15 +667,13 @@ function build() {
|
|
|
431
667
|
console.log('====================================');
|
|
432
668
|
console.log('');
|
|
433
669
|
|
|
434
|
-
// Locate skills directory
|
|
670
|
+
// ── Step 0: Locate skills directory ──
|
|
435
671
|
const packageDir = path.resolve(__dirname, '..');
|
|
436
672
|
let skillsRoot = path.join(packageDir, 'github-assets', 'skills');
|
|
437
673
|
|
|
438
|
-
// If running from installed package, check node_modules path
|
|
439
674
|
if (!fs.existsSync(skillsRoot)) {
|
|
440
675
|
skillsRoot = path.join(packageDir, '.github', 'skills');
|
|
441
676
|
}
|
|
442
|
-
// Also check project's .github/skills (after init)
|
|
443
677
|
if (!fs.existsSync(skillsRoot)) {
|
|
444
678
|
skillsRoot = path.join(process.cwd(), '.github', 'skills');
|
|
445
679
|
}
|
|
@@ -450,14 +684,17 @@ function build() {
|
|
|
450
684
|
process.exit(1);
|
|
451
685
|
}
|
|
452
686
|
|
|
453
|
-
// Read package version
|
|
454
687
|
let version = '1.0.0';
|
|
455
688
|
try {
|
|
456
689
|
const pkg = JSON.parse(fs.readFileSync(path.join(packageDir, 'package.json'), 'utf-8'));
|
|
457
690
|
version = pkg.version || version;
|
|
458
691
|
} catch { /* ignore */ }
|
|
459
692
|
|
|
460
|
-
// Discover skills
|
|
693
|
+
// ── Step 1: Discover & validate skills ──
|
|
694
|
+
console.log('Step 1: Discovering and validating skills...');
|
|
695
|
+
console.log(`📂 Skills root: ${skillsRoot}`);
|
|
696
|
+
console.log('');
|
|
697
|
+
|
|
461
698
|
const skillDirs = fs.readdirSync(skillsRoot, { withFileTypes: true })
|
|
462
699
|
.filter(d => d.isDirectory() && d.name.startsWith('shikigami-'))
|
|
463
700
|
.map(d => d.name);
|
|
@@ -467,11 +704,6 @@ function build() {
|
|
|
467
704
|
process.exit(1);
|
|
468
705
|
}
|
|
469
706
|
|
|
470
|
-
console.log(`📂 Skills root: ${skillsRoot}`);
|
|
471
|
-
console.log(`📦 Found ${skillDirs.length} skills`);
|
|
472
|
-
console.log('');
|
|
473
|
-
|
|
474
|
-
// Validate all skills
|
|
475
707
|
let hasErrors = false;
|
|
476
708
|
const validatedSkills = [];
|
|
477
709
|
|
|
@@ -486,13 +718,19 @@ function build() {
|
|
|
486
718
|
for (const warn of result.warnings) console.log(` ⚠️ ${warn}`);
|
|
487
719
|
|
|
488
720
|
if (result.errors.length === 0) {
|
|
489
|
-
const
|
|
490
|
-
|
|
721
|
+
const origCount = result.originalCompanions ? result.originalCompanions.length : 0;
|
|
722
|
+
const pkgCount = result.packagedCompanions ? result.packagedCompanions.length : 0;
|
|
723
|
+
const consolidatedNote = origCount > MAX_COMPANIONS_PER_SKILL
|
|
724
|
+
? ` (consolidated ${origCount} → ${pkgCount})`
|
|
725
|
+
: '';
|
|
726
|
+
console.log(` ✅ ${skillName} (${pkgCount} companion files${consolidatedNote})`);
|
|
491
727
|
validatedSkills.push({
|
|
492
728
|
name: skillName,
|
|
493
729
|
dir: skillDir,
|
|
494
|
-
|
|
730
|
+
originalCompanions: result.originalCompanions,
|
|
731
|
+
packagedCompanions: result.packagedCompanions,
|
|
495
732
|
frontmatter: result.frontmatter,
|
|
733
|
+
content: result.content,
|
|
496
734
|
});
|
|
497
735
|
}
|
|
498
736
|
}
|
|
@@ -505,63 +743,89 @@ function build() {
|
|
|
505
743
|
}
|
|
506
744
|
|
|
507
745
|
if (validatedSkills.length > MAX_SKILLS) {
|
|
508
|
-
console.error(`❌ Too many skills: ${validatedSkills.length} (max ${MAX_SKILLS})`);
|
|
746
|
+
console.error(`❌ [ASKILL-M002] Too many skills: ${validatedSkills.length} (max ${MAX_SKILLS})`);
|
|
747
|
+
process.exit(1);
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
// ── Manifest validation ──
|
|
751
|
+
const manifest = generateManifest(validatedSkills, version);
|
|
752
|
+
const manifestErrors = validateManifest(manifest);
|
|
753
|
+
if (manifestErrors.length > 0) {
|
|
754
|
+
hasErrors = true;
|
|
755
|
+
for (const err of manifestErrors) console.log(` ❌ ${err}`);
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
if (hasErrors) {
|
|
759
|
+
console.error('❌ Manifest validation failed.');
|
|
509
760
|
process.exit(1);
|
|
510
761
|
}
|
|
511
762
|
|
|
512
763
|
if (check) {
|
|
513
764
|
console.log('✅ Validation passed (dry-run mode)');
|
|
514
765
|
console.log(` ${validatedSkills.length} skills ready for packaging`);
|
|
766
|
+
console.log('');
|
|
767
|
+
console.log('Cross-platform compatibility:');
|
|
768
|
+
console.log(' ✅ GitHub Copilot CLI');
|
|
769
|
+
console.log(' ✅ VS Code Copilot');
|
|
770
|
+
console.log(' ✅ Microsoft 365 Copilot Cowork');
|
|
771
|
+
console.log(' ✅ Claude Code / Gemini CLI / Cursor');
|
|
515
772
|
process.exit(0);
|
|
516
773
|
}
|
|
517
774
|
|
|
518
|
-
// Build package
|
|
519
|
-
console.log('
|
|
775
|
+
// ── Step 2-5: Build package ──
|
|
776
|
+
console.log('Step 2: Building Cowork plugin package...');
|
|
520
777
|
|
|
521
778
|
const zipFiles = [];
|
|
522
779
|
|
|
523
|
-
//
|
|
524
|
-
const manifest = generateManifest(validatedSkills, version);
|
|
780
|
+
// Step 4: manifest.json
|
|
525
781
|
zipFiles.push({
|
|
526
782
|
path: 'manifest.json',
|
|
527
783
|
content: JSON.stringify(manifest, null, 2),
|
|
528
784
|
});
|
|
529
785
|
console.log(' ✅ manifest.json');
|
|
530
786
|
|
|
531
|
-
//
|
|
532
|
-
const colorPng = createMinimalPNG(192, 192, 107, 70, 193);
|
|
787
|
+
// Step 5: Icons
|
|
788
|
+
const colorPng = createMinimalPNG(192, 192, 107, 70, 193);
|
|
533
789
|
const outlinePng = createMinimalPNG(32, 32, 107, 70, 193);
|
|
534
790
|
zipFiles.push({ path: 'color.png', content: colorPng });
|
|
535
791
|
zipFiles.push({ path: 'outline.png', content: outlinePng });
|
|
536
792
|
console.log(' ✅ color.png (192×192)');
|
|
537
793
|
console.log(' ✅ outline.png (32×32)');
|
|
538
794
|
|
|
539
|
-
//
|
|
795
|
+
// Steps 1-2: Skills with Cowork-specific SKILL.md
|
|
540
796
|
for (const skill of validatedSkills) {
|
|
541
|
-
//
|
|
542
|
-
const
|
|
797
|
+
// Rewrite SKILL.md for Cowork (add metadata, category, icon, update paths)
|
|
798
|
+
const rewrittenSkillMd = rewriteSkillMdForCowork(
|
|
799
|
+
skill.content,
|
|
800
|
+
skill.originalCompanions,
|
|
801
|
+
skill.packagedCompanions,
|
|
802
|
+
skill.name
|
|
803
|
+
);
|
|
804
|
+
|
|
543
805
|
zipFiles.push({
|
|
544
806
|
path: `skills/${skill.name}/SKILL.md`,
|
|
545
|
-
content:
|
|
807
|
+
content: rewrittenSkillMd,
|
|
546
808
|
});
|
|
547
809
|
|
|
548
810
|
// Copy companion files (already consolidated)
|
|
549
|
-
for (const companion of skill.
|
|
811
|
+
for (const companion of skill.packagedCompanions) {
|
|
550
812
|
zipFiles.push({
|
|
551
813
|
path: `skills/${skill.name}/${companion.relativePath}`,
|
|
552
814
|
content: companion.content,
|
|
553
815
|
});
|
|
554
816
|
}
|
|
555
817
|
|
|
556
|
-
console.log(` ✅ skills/${skill.name}/ (${skill.
|
|
818
|
+
console.log(` ✅ skills/${skill.name}/ (${skill.packagedCompanions.length + 1} files)`);
|
|
557
819
|
}
|
|
558
820
|
|
|
559
|
-
//
|
|
821
|
+
// ── Step 6: Create ZIP ──
|
|
822
|
+
console.log('');
|
|
823
|
+
console.log('Step 3: Creating ZIP package...');
|
|
824
|
+
|
|
560
825
|
const outDir = path.resolve(out);
|
|
561
826
|
fs.mkdirSync(outDir, { recursive: true });
|
|
562
827
|
const zipPath = path.join(outDir, 'shikigami-cowork-plugin.zip');
|
|
563
828
|
|
|
564
|
-
// Remove old zip if exists
|
|
565
829
|
if (fs.existsSync(zipPath)) {
|
|
566
830
|
fs.unlinkSync(zipPath);
|
|
567
831
|
}
|
|
@@ -570,15 +834,23 @@ function build() {
|
|
|
570
834
|
|
|
571
835
|
const zipSize = fs.statSync(zipPath).size;
|
|
572
836
|
console.log('');
|
|
573
|
-
console.log(
|
|
574
|
-
console.log(
|
|
575
|
-
console.log(`
|
|
576
|
-
console.log(`
|
|
837
|
+
console.log('✅ Plugin package created!');
|
|
838
|
+
console.log('');
|
|
839
|
+
console.log(` 📦 ${zipPath}`);
|
|
840
|
+
console.log(` 📏 Size: ${(zipSize / 1024).toFixed(1)} KB`);
|
|
841
|
+
console.log(` 🔧 Skills: ${validatedSkills.length}`);
|
|
842
|
+
console.log(` 📋 Version: ${version}`);
|
|
843
|
+
console.log('');
|
|
844
|
+
console.log('Cross-platform compatibility:');
|
|
845
|
+
console.log(' ✅ GitHub Copilot CLI — use `npx shikigami init`');
|
|
846
|
+
console.log(' ✅ VS Code Copilot — same SKILL.md format');
|
|
847
|
+
console.log(' ✅ Copilot Cowork — upload .zip to M365 Admin Center');
|
|
848
|
+
console.log(' ✅ Claude Code — same SKILL.md format');
|
|
577
849
|
console.log('');
|
|
578
|
-
console.log('📝 Next steps:');
|
|
579
|
-
console.log('
|
|
580
|
-
console.log('
|
|
581
|
-
console.log('
|
|
850
|
+
console.log('📝 Next steps (Copilot Cowork):');
|
|
851
|
+
console.log(' 1. Open M365 Admin Center > Manage Apps > Upload Custom App');
|
|
852
|
+
console.log(' 2. Upload shikigami-cowork-plugin.zip');
|
|
853
|
+
console.log(' 3. Open Cowork > Sources & Skills to verify');
|
|
582
854
|
console.log('');
|
|
583
855
|
console.log('📖 https://learn.microsoft.com/microsoft-365/copilot/cowork/cowork-plugin-development');
|
|
584
856
|
console.log('');
|