sparkle-design-cli 1.6.2 → 2.0.0
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 +55 -15
- package/bin/sparkle-design.js +25 -2
- package/lib/font-manager.js +1 -1
- package/lib/setup.js +224 -35
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,27 @@
|
|
|
1
1
|
# Sparkle Design CLI
|
|
2
2
|
|
|
3
|
-
Sparkle Design
|
|
3
|
+
Sparkle Design のプロジェクト初期セットアップ、CSS 生成、導入先プロジェクト向けのアンチパターン検査、AI エージェント向け guard 設定を行う CLI ツールです。
|
|
4
|
+
|
|
5
|
+
## クイックスタート
|
|
6
|
+
|
|
7
|
+
既存の Next.js / Vite プロジェクトに Sparkle Design を導入する場合、以下 1 コマンドで完了します:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npx --yes sparkle-design-cli setup --assistant claude
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
setup は次を自動で行います:
|
|
14
|
+
|
|
15
|
+
1. パッケージマネージャー(pnpm / npm / yarn / bun)を自動検出
|
|
16
|
+
2. `sparkle-design` を dependencies、`tailwindcss` + `@tailwindcss/postcss` を devDependencies に追加
|
|
17
|
+
3. 未作成の場合のみ初期ファイルを生成:
|
|
18
|
+
- `sparkle.config.json`(デフォルトは blue / Inter / JetBrains Mono / md)
|
|
19
|
+
- `postcss.config.mjs`
|
|
20
|
+
- `src/app/globals.css`(または `src/globals.css`)
|
|
21
|
+
4. AI 指示ファイル(`CLAUDE.md` / `AGENTS.md` / Cursor rules)に Sparkle Design Guard ブロックを追加
|
|
22
|
+
5. `sparkle-design-cli generate` を実行し、`sparkle-design.css` と `SparkleHead.tsx` を生成
|
|
23
|
+
|
|
24
|
+
既存ファイルは上書きされません。
|
|
4
25
|
|
|
5
26
|
## インストール
|
|
6
27
|
|
|
@@ -8,11 +29,16 @@ Sparkle Design の CSS 生成、導入先プロジェクト向けのアンチパ
|
|
|
8
29
|
npm install -g sparkle-design-cli
|
|
9
30
|
```
|
|
10
31
|
|
|
32
|
+
> `npx --yes sparkle-design-cli ...` で都度実行する運用を推奨します(常に最新版が利用されます)。
|
|
33
|
+
|
|
11
34
|
## 使用方法
|
|
12
35
|
|
|
13
36
|
### サブコマンド
|
|
14
37
|
|
|
15
38
|
```bash
|
|
39
|
+
# プロジェクト全体を一気にセットアップ(推奨・新規導入時)
|
|
40
|
+
npx --yes sparkle-design-cli setup --assistant claude
|
|
41
|
+
|
|
16
42
|
# CSS を生成
|
|
17
43
|
npx sparkle-design-cli generate
|
|
18
44
|
|
|
@@ -21,9 +47,6 @@ npx sparkle-design-cli check src --strict
|
|
|
21
47
|
|
|
22
48
|
# AI 向けに JSON で検査結果と手動確認項目を取得
|
|
23
49
|
npx sparkle-design-cli check src --format json
|
|
24
|
-
|
|
25
|
-
# Claude / Codex / Cursor 向けの guard 設定を差し込む
|
|
26
|
-
npx --yes sparkle-design-cli setup --assistant claude
|
|
27
50
|
```
|
|
28
51
|
|
|
29
52
|
### 後方互換モード
|
|
@@ -141,12 +164,18 @@ sparkle-design-cli check --help
|
|
|
141
164
|
|
|
142
165
|
AI エージェントや CI からこの `lint:sparkle` を呼ぶ運用にすると、ガイドラインの注意書きだけに頼らず機械的に検査できます。
|
|
143
166
|
|
|
144
|
-
### setup:
|
|
167
|
+
### setup: プロジェクトのフルセットアップ
|
|
168
|
+
|
|
169
|
+
`sparkle-design-cli setup` は、Sparkle Design の導入に必要な作業をまとめて行います:
|
|
145
170
|
|
|
146
|
-
|
|
171
|
+
1. **パッケージマネージャーの検出**(pnpm / yarn / bun / npm)
|
|
172
|
+
2. **パッケージのインストール** — `sparkle-design`(dependencies)、`tailwindcss` + `@tailwindcss/postcss`(devDependencies)
|
|
173
|
+
3. **初期ファイルの生成** — `sparkle.config.json` / `postcss.config.mjs` / `globals.css` が無い場合のみ作成
|
|
174
|
+
4. **AI ガード設定** — `CLAUDE.md` などに Sparkle Design Guard ブロックと `lint:sparkle` script を追加
|
|
175
|
+
5. **`generate` の実行** — `sparkle-design.css` と `SparkleHead.tsx` を生成
|
|
147
176
|
|
|
148
177
|
```bash
|
|
149
|
-
# Claude Code
|
|
178
|
+
# Claude Code 向けフルセットアップ
|
|
150
179
|
npx --yes sparkle-design-cli setup --assistant claude
|
|
151
180
|
|
|
152
181
|
# Codex / AGENTS.md 向け
|
|
@@ -155,16 +184,16 @@ npx --yes sparkle-design-cli setup --assistant codex
|
|
|
155
184
|
# Cursor rules 向け
|
|
156
185
|
npx --yes sparkle-design-cli setup --assistant cursor
|
|
157
186
|
|
|
158
|
-
#
|
|
159
|
-
npx --yes sparkle-design-cli setup --assistant
|
|
187
|
+
# AI ガードだけ追加したい場合(パッケージインストール・scaffold・generate をスキップ)
|
|
188
|
+
npx --yes sparkle-design-cli setup --assistant claude --skip-install --skip-scaffold --skip-generate
|
|
160
189
|
```
|
|
161
190
|
|
|
162
|
-
|
|
163
|
-
- `
|
|
164
|
-
-
|
|
165
|
-
-
|
|
191
|
+
既存ファイルは上書きされません:
|
|
192
|
+
- `sparkle.config.json` / `postcss.config.*` / `globals.css` が存在する場合はそのまま保持
|
|
193
|
+
- 既に依存関係に入っているパッケージはインストールをスキップ
|
|
194
|
+
- `package.json` の既存の `lint:sparkle*` 独自 script は保持(`--force-script-update` で上書き)
|
|
166
195
|
|
|
167
|
-
`--target`
|
|
196
|
+
`--target` を省略した場合、lint 対象は `src`, `src/components`, `src/features`, `app`, `components` の順で自動検出されます。
|
|
168
197
|
|
|
169
198
|
利用できる assistant:
|
|
170
199
|
- `claude`: `CLAUDE.md`
|
|
@@ -172,6 +201,17 @@ npx --yes sparkle-design-cli setup --assistant generic
|
|
|
172
201
|
- `cursor`: `.cursor/rules/sparkle-design-guard.mdc`
|
|
173
202
|
- `generic`: `SPARKLE-DESIGN-AI.md`
|
|
174
203
|
|
|
204
|
+
#### setup オプション一覧
|
|
205
|
+
|
|
206
|
+
- `--assistant <name>`: claude / codex / cursor / generic (default: generic)
|
|
207
|
+
- `--target <path>`: lint 対象パス (default: auto-detect)
|
|
208
|
+
- `--instructions-path <path>`: AI 指示ファイルの出力先パス (default: アシスタント別のデフォルト)
|
|
209
|
+
- `--skip-install`: パッケージインストールをスキップ
|
|
210
|
+
- `--skip-scaffold`: 初期ファイル生成をスキップ
|
|
211
|
+
- `--skip-generate`: `generate` 実行をスキップ
|
|
212
|
+
- `--dry-run`: ファイルを変更せず結果だけ表示
|
|
213
|
+
- `--force-script-update`: 既存の `lint:sparkle` 系 script も上書き
|
|
214
|
+
|
|
175
215
|
`setup` は通常実行時も JSON サマリーを stdout に表示します。`--dry-run` を付けると、その JSON を表示したままファイル変更だけを抑止します。
|
|
176
216
|
|
|
177
217
|
## 設定ファイル (sparkle.config.json)
|
|
@@ -238,7 +278,7 @@ Figma プラグインが出力する基本4項目に加えて、プロジェク
|
|
|
238
278
|
|
|
239
279
|
#### extend.source-packages
|
|
240
280
|
|
|
241
|
-
|
|
281
|
+
`sparkle-design` を npm パッケージとして利用する場合に必須。`globals.css` に `@source` ディレクティブを自動挿入します。`sparkle-design` は常にデフォルトで含まれます。
|
|
242
282
|
|
|
243
283
|
#### extend.custom-css
|
|
244
284
|
|
package/bin/sparkle-design.js
CHANGED
|
@@ -83,6 +83,9 @@ function parseSetupOptions(args) {
|
|
|
83
83
|
instructionsPath: null,
|
|
84
84
|
dryRun: false,
|
|
85
85
|
forceScriptUpdate: false,
|
|
86
|
+
skipInstall: false,
|
|
87
|
+
skipScaffold: false,
|
|
88
|
+
skipGenerate: false,
|
|
86
89
|
help: false,
|
|
87
90
|
};
|
|
88
91
|
|
|
@@ -104,6 +107,12 @@ function parseSetupOptions(args) {
|
|
|
104
107
|
options.dryRun = true;
|
|
105
108
|
} else if (arg === '--force-script-update') {
|
|
106
109
|
options.forceScriptUpdate = true;
|
|
110
|
+
} else if (arg === '--skip-install') {
|
|
111
|
+
options.skipInstall = true;
|
|
112
|
+
} else if (arg === '--skip-scaffold') {
|
|
113
|
+
options.skipScaffold = true;
|
|
114
|
+
} else if (arg === '--skip-generate') {
|
|
115
|
+
options.skipGenerate = true;
|
|
107
116
|
} else {
|
|
108
117
|
throw new Error(`Unknown option for setup: ${arg}`);
|
|
109
118
|
}
|
|
@@ -122,7 +131,7 @@ Sparkle Design CLI
|
|
|
122
131
|
Commands:
|
|
123
132
|
generate sparkle.config.json から CSS を生成
|
|
124
133
|
check Sparkle Design のアンチパターンを検査
|
|
125
|
-
setup AI
|
|
134
|
+
setup Sparkle Design プロジェクトをセットアップ(パッケージ導入 + 初期ファイル + AI ガード + generate)
|
|
126
135
|
|
|
127
136
|
後方互換:
|
|
128
137
|
sparkle-design-cli [generate options]
|
|
@@ -142,7 +151,11 @@ Check:
|
|
|
142
151
|
sparkle-design-cli check src/components src/features
|
|
143
152
|
|
|
144
153
|
Setup:
|
|
145
|
-
sparkle-design-cli setup
|
|
154
|
+
sparkle-design-cli setup # フルセットアップ(推奨・新規導入時)
|
|
155
|
+
sparkle-design-cli setup --assistant claude # Claude 向けガードも同時にセットアップ
|
|
156
|
+
sparkle-design-cli setup --skip-install # パッケージインストールをスキップ
|
|
157
|
+
sparkle-design-cli setup --skip-scaffold # 初期ファイル生成をスキップ
|
|
158
|
+
sparkle-design-cli setup --skip-generate # generate 実行をスキップ
|
|
146
159
|
sparkle-design-cli setup --assistant codex --target src
|
|
147
160
|
sparkle-design-cli setup --assistant cursor --dry-run
|
|
148
161
|
sparkle-design-cli setup --assistant claude --force-script-update
|
|
@@ -193,6 +206,16 @@ Setup options:
|
|
|
193
206
|
--dry-run ファイルを変更せず結果だけ表示
|
|
194
207
|
--instructions-path <path> AI 指示ファイルの出力先パス (default: アシスタント別のデフォルト)
|
|
195
208
|
--force-script-update 既存の lint:sparkle 系 script も上書きする
|
|
209
|
+
--skip-install パッケージインストール(sparkle-design, tailwindcss)をスキップ
|
|
210
|
+
--skip-scaffold 初期ファイル(sparkle.config.json, postcss.config, globals.css)生成をスキップ
|
|
211
|
+
--skip-generate generate 実行をスキップ
|
|
212
|
+
|
|
213
|
+
Setup の動作:
|
|
214
|
+
1. パッケージマネージャー検出(pnpm / npm / yarn / bun)
|
|
215
|
+
2. sparkle-design を dependencies に追加、tailwindcss + @tailwindcss/postcss を devDependencies に追加
|
|
216
|
+
3. sparkle.config.json / postcss.config.mjs / globals.css が無ければ初期ファイルを生成
|
|
217
|
+
4. AI アシスタントのガードブロックを指定ファイルに挿入(存在しなければ新規作成)
|
|
218
|
+
5. sparkle-design-cli generate を実行し、sparkle-design.css と SparkleHead.tsx を生成
|
|
196
219
|
|
|
197
220
|
Check で検出する主なパターン:
|
|
198
221
|
- フォーム入力に Dialog を使っている
|
package/lib/font-manager.js
CHANGED
|
@@ -66,7 +66,7 @@ function resolveNodeModulesRelPath(globalsPath) {
|
|
|
66
66
|
* @returns {string} @source ディレクティブブロック
|
|
67
67
|
*/
|
|
68
68
|
function createSourceBlock(sourcePackages = [], nodeModulesRel = '../node_modules') {
|
|
69
|
-
const defaultPackage = '
|
|
69
|
+
const defaultPackage = 'sparkle-design';
|
|
70
70
|
const allPackages = [defaultPackage, ...sourcePackages.filter(p => p !== defaultPackage)];
|
|
71
71
|
const sourceLines = allPackages.map(pkg => `@source "${nodeModulesRel}/${pkg}/dist";`);
|
|
72
72
|
return [COMMENTS.SOURCE_DIRECTIVE, ...sourceLines].join('\n');
|
package/lib/setup.js
CHANGED
|
@@ -1,5 +1,49 @@
|
|
|
1
1
|
import fs from 'fs';
|
|
2
2
|
import path from 'path';
|
|
3
|
+
import { spawnSync } from 'child_process';
|
|
4
|
+
import { generateCSS } from './generate-css.js';
|
|
5
|
+
|
|
6
|
+
// デフォルトの sparkle.config.json テンプレート
|
|
7
|
+
// en: Default sparkle.config.json template
|
|
8
|
+
const DEFAULT_SPARKLE_CONFIG = {
|
|
9
|
+
primary: 'blue',
|
|
10
|
+
'font-pro': 'Inter',
|
|
11
|
+
'font-mono': 'JetBrains Mono',
|
|
12
|
+
radius: 'md',
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
// 初期 globals.css のテンプレート
|
|
16
|
+
// en: Initial globals.css template
|
|
17
|
+
const INITIAL_GLOBALS_CSS = `@import "tailwindcss";
|
|
18
|
+
@import "./sparkle-design.css";
|
|
19
|
+
`;
|
|
20
|
+
|
|
21
|
+
// インストール対象パッケージ
|
|
22
|
+
// en: Packages to install
|
|
23
|
+
const SPARKLE_PACKAGES = ['sparkle-design'];
|
|
24
|
+
const TAILWIND_PACKAGES = ['tailwindcss', '@tailwindcss/postcss'];
|
|
25
|
+
|
|
26
|
+
// 初期 postcss.config.mjs のテンプレート
|
|
27
|
+
// en: Initial postcss.config.mjs template
|
|
28
|
+
const INITIAL_POSTCSS_CONFIG = `/** @type {import('postcss-load-config').Config} */
|
|
29
|
+
const config = {
|
|
30
|
+
plugins: {
|
|
31
|
+
'@tailwindcss/postcss': {},
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export default config;
|
|
36
|
+
`;
|
|
37
|
+
|
|
38
|
+
// globals.css 候補パス(優先順)
|
|
39
|
+
// en: globals.css candidate paths in priority order
|
|
40
|
+
const GLOBALS_CSS_CANDIDATES = [
|
|
41
|
+
'src/app/globals.css',
|
|
42
|
+
'app/globals.css',
|
|
43
|
+
'src/globals.css',
|
|
44
|
+
'src/index.css',
|
|
45
|
+
'src/styles/globals.css',
|
|
46
|
+
];
|
|
3
47
|
|
|
4
48
|
const ASSISTANT_CONFIG = {
|
|
5
49
|
claude: {
|
|
@@ -27,10 +71,6 @@ function buildCheckScript(target, mode) {
|
|
|
27
71
|
}
|
|
28
72
|
|
|
29
73
|
|
|
30
|
-
function escapeRegExp(value) {
|
|
31
|
-
return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
32
|
-
}
|
|
33
|
-
|
|
34
74
|
function ensureDir(filePath) {
|
|
35
75
|
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
36
76
|
}
|
|
@@ -221,64 +261,213 @@ function updatePackageJson(filePath, target, force) {
|
|
|
221
261
|
};
|
|
222
262
|
}
|
|
223
263
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
264
|
+
// パッケージマネージャーごとの add サブコマンドと -D フラグ
|
|
265
|
+
// en: Per-package-manager add subcommand and dev flag
|
|
266
|
+
const PM_COMMANDS = {
|
|
267
|
+
pnpm: { add: 'add', dev: '-D' },
|
|
268
|
+
yarn: { add: 'add', dev: '-D' },
|
|
269
|
+
bun: { add: 'add', dev: '-d' },
|
|
270
|
+
npm: { add: 'install', dev: '--save-dev' },
|
|
271
|
+
};
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* パッケージマネージャーを lockfile から検出する
|
|
275
|
+
* en: Detect package manager from lock files
|
|
276
|
+
*/
|
|
277
|
+
function detectPackageManager(cwd) {
|
|
278
|
+
if (fs.existsSync(path.join(cwd, 'pnpm-lock.yaml'))) return 'pnpm';
|
|
279
|
+
if (fs.existsSync(path.join(cwd, 'bun.lockb')) || fs.existsSync(path.join(cwd, 'bun.lock'))) return 'bun';
|
|
280
|
+
if (fs.existsSync(path.join(cwd, 'yarn.lock'))) return 'yarn';
|
|
281
|
+
return 'npm';
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* package.json に未登録のパッケージだけをインストールする
|
|
286
|
+
* en: Install only packages not yet listed in package.json
|
|
287
|
+
*/
|
|
288
|
+
function ensurePackagesInstalled(cwd, packageManager, candidates, { dev = false, dryRun = false } = {}) {
|
|
289
|
+
const packageJson = readJson(path.join(cwd, 'package.json'));
|
|
290
|
+
const deps = { ...(packageJson.dependencies ?? {}), ...(packageJson.devDependencies ?? {}) };
|
|
291
|
+
const missing = candidates.filter((pkg) => !Object.prototype.hasOwnProperty.call(deps, pkg));
|
|
230
292
|
|
|
231
|
-
if (
|
|
232
|
-
|
|
293
|
+
if (missing.length === 0) {
|
|
294
|
+
return { ran: false, reason: 'already-installed', packages: [] };
|
|
295
|
+
}
|
|
296
|
+
if (dryRun) {
|
|
297
|
+
return { ran: false, reason: 'dry-run', packages: missing, packageManager, dev };
|
|
233
298
|
}
|
|
234
299
|
|
|
235
|
-
const
|
|
236
|
-
const
|
|
237
|
-
? normalizePath(cwd, options.target, 'Target path')
|
|
238
|
-
: normalizePath(cwd, detectTarget(cwd), 'Target path');
|
|
300
|
+
const pm = PM_COMMANDS[packageManager];
|
|
301
|
+
const args = [pm.add, ...(dev ? [pm.dev] : []), ...missing];
|
|
239
302
|
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
303
|
+
console.log(`📦 ${packageManager} ${args.join(' ')}`);
|
|
304
|
+
const result = spawnSync(packageManager, args, { cwd, stdio: 'inherit' });
|
|
305
|
+
if (result.status !== 0) {
|
|
306
|
+
throw new Error(`Failed to install packages via ${packageManager}: ${missing.join(', ')}`);
|
|
243
307
|
}
|
|
244
308
|
|
|
309
|
+
return { ran: true, reason: 'installed', packages: missing, packageManager, dev };
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* src/app → src/app/globals.css、src 直下があれば src/globals.css を優先
|
|
314
|
+
* en: Prefer src/app/globals.css when src/app exists, otherwise src/globals.css
|
|
315
|
+
*/
|
|
316
|
+
function defaultGlobalsCssTarget(cwd) {
|
|
317
|
+
if (fs.existsSync(path.join(cwd, 'src/app'))) return 'src/app/globals.css';
|
|
318
|
+
if (fs.existsSync(path.join(cwd, 'src'))) return 'src/globals.css';
|
|
319
|
+
return 'src/app/globals.css';
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
// スキャフォールド対象のファイル定義
|
|
323
|
+
// en: Scaffold targets: existing候補の1つでもヒットすれば作成をスキップ
|
|
324
|
+
function buildScaffoldTargets(cwd) {
|
|
325
|
+
return [
|
|
326
|
+
{
|
|
327
|
+
name: 'sparkleConfig',
|
|
328
|
+
candidates: ['sparkle.config.json'],
|
|
329
|
+
target: 'sparkle.config.json',
|
|
330
|
+
write: (abs) => writeJson(abs, DEFAULT_SPARKLE_CONFIG),
|
|
331
|
+
},
|
|
332
|
+
{
|
|
333
|
+
name: 'postcssConfig',
|
|
334
|
+
candidates: ['postcss.config.mjs', 'postcss.config.js', 'postcss.config.cjs', 'postcss.config.ts'],
|
|
335
|
+
target: 'postcss.config.mjs',
|
|
336
|
+
write: (abs) => fs.writeFileSync(abs, INITIAL_POSTCSS_CONFIG, 'utf8'),
|
|
337
|
+
},
|
|
338
|
+
{
|
|
339
|
+
name: 'globalsCss',
|
|
340
|
+
candidates: GLOBALS_CSS_CANDIDATES,
|
|
341
|
+
target: defaultGlobalsCssTarget(cwd),
|
|
342
|
+
write: (abs) => {
|
|
343
|
+
ensureDir(abs);
|
|
344
|
+
fs.writeFileSync(abs, INITIAL_GLOBALS_CSS, 'utf8');
|
|
345
|
+
},
|
|
346
|
+
},
|
|
347
|
+
];
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
/**
|
|
351
|
+
* 各スキャフォールド対象について、候補が1つでも存在すればスキップ、無ければ target に書き込む
|
|
352
|
+
* en: Scaffold each target: skip if any candidate exists, otherwise write to target
|
|
353
|
+
*/
|
|
354
|
+
function scaffoldAll(cwd, { dryRun = false } = {}) {
|
|
355
|
+
const results = {};
|
|
356
|
+
for (const spec of buildScaffoldTargets(cwd)) {
|
|
357
|
+
const existing = spec.candidates.find((c) => fs.existsSync(path.join(cwd, c)));
|
|
358
|
+
if (existing) {
|
|
359
|
+
results[spec.name] = { created: false, existed: true, path: existing };
|
|
360
|
+
continue;
|
|
361
|
+
}
|
|
362
|
+
if (!dryRun) spec.write(path.join(cwd, spec.target));
|
|
363
|
+
results[spec.name] = { created: true, existed: false, path: spec.target };
|
|
364
|
+
}
|
|
365
|
+
return results;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
// --- パイプライン各フェーズ ---
|
|
369
|
+
// en: Pipeline phases
|
|
370
|
+
|
|
371
|
+
function runInstall(cwd, packageManager, { skipInstall, dryRun }) {
|
|
372
|
+
if (skipInstall) return { skipped: true, sparkle: null, tailwind: null };
|
|
373
|
+
|
|
374
|
+
console.log(`🔍 パッケージマネージャーを検出: ${packageManager}`);
|
|
375
|
+
return {
|
|
376
|
+
skipped: false,
|
|
377
|
+
sparkle: ensurePackagesInstalled(cwd, packageManager, SPARKLE_PACKAGES, { dryRun }),
|
|
378
|
+
tailwind: ensurePackagesInstalled(cwd, packageManager, TAILWIND_PACKAGES, { dev: true, dryRun }),
|
|
379
|
+
};
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
function runScaffold(cwd, { skipScaffold, dryRun }) {
|
|
383
|
+
if (skipScaffold) return { skipped: true };
|
|
384
|
+
return { skipped: false, ...scaffoldAll(cwd, { dryRun }) };
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
function runAssistantGuard(cwd, packageJsonPath, options, assistantConfig) {
|
|
388
|
+
const force = Boolean(options.forceScriptUpdate);
|
|
389
|
+
const target = options.target
|
|
390
|
+
? normalizePath(cwd, options.target, 'Target path')
|
|
391
|
+
: normalizePath(cwd, detectTarget(cwd), 'Target path');
|
|
245
392
|
const instructionPath = options.instructionsPath
|
|
246
393
|
? path.resolve(cwd, normalizePath(cwd, options.instructionsPath, 'Instructions path', { checkParentOnly: true }))
|
|
247
394
|
: path.resolve(cwd, assistantConfig.path);
|
|
395
|
+
|
|
248
396
|
const packageResult = updatePackageJson(packageJsonPath, target, force);
|
|
249
|
-
const instructionBlock = buildInstructionBlock(target, assistant);
|
|
397
|
+
const instructionBlock = buildInstructionBlock(target, options.assistant);
|
|
250
398
|
const instructionResult = updateInstructionFile(instructionPath, instructionBlock);
|
|
251
399
|
|
|
252
|
-
if (!dryRun) {
|
|
253
|
-
if (packageResult.changed)
|
|
254
|
-
writeJson(packageJsonPath, packageResult.packageJson);
|
|
255
|
-
}
|
|
256
|
-
|
|
400
|
+
if (!options.dryRun) {
|
|
401
|
+
if (packageResult.changed) writeJson(packageJsonPath, packageResult.packageJson);
|
|
257
402
|
if (instructionResult.changed) {
|
|
258
403
|
ensureDir(instructionPath);
|
|
259
404
|
fs.writeFileSync(instructionPath, instructionResult.content, 'utf8');
|
|
260
405
|
}
|
|
261
406
|
}
|
|
262
407
|
|
|
408
|
+
return {
|
|
409
|
+
target,
|
|
410
|
+
targetSource: options.target ? 'explicit' : 'auto',
|
|
411
|
+
instructionPath,
|
|
412
|
+
packageResult,
|
|
413
|
+
instructionResult,
|
|
414
|
+
};
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
function runGenerate({ skipGenerate, dryRun }) {
|
|
418
|
+
if (skipGenerate || dryRun) return { skipped: true, ran: false };
|
|
419
|
+
try {
|
|
420
|
+
console.log('🎨 sparkle-design.css を生成中...');
|
|
421
|
+
generateCSS();
|
|
422
|
+
return { skipped: false, ran: true };
|
|
423
|
+
} catch (error) {
|
|
424
|
+
console.warn(`⚠️ generate 実行をスキップしました: ${error.message}`);
|
|
425
|
+
return { skipped: false, ran: false, error: error.message };
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
export function setupAssistant(options = {}) {
|
|
430
|
+
const assistant = options.assistant ?? 'generic';
|
|
431
|
+
const assistantConfig = ASSISTANT_CONFIG[assistant];
|
|
432
|
+
if (!assistantConfig) throw new Error(`Unsupported assistant: ${assistant}`);
|
|
433
|
+
|
|
434
|
+
const cwd = process.cwd();
|
|
435
|
+
const packageJsonPath = path.resolve(cwd, 'package.json');
|
|
436
|
+
if (!fs.existsSync(packageJsonPath)) {
|
|
437
|
+
throw new Error(`package.json not found in ${cwd}`);
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
const dryRun = Boolean(options.dryRun);
|
|
441
|
+
const packageManager = detectPackageManager(cwd);
|
|
442
|
+
|
|
443
|
+
const install = runInstall(cwd, packageManager, { skipInstall: Boolean(options.skipInstall), dryRun });
|
|
444
|
+
const scaffold = runScaffold(cwd, { skipScaffold: Boolean(options.skipScaffold), dryRun });
|
|
445
|
+
const guard = runAssistantGuard(cwd, packageJsonPath, { ...options, assistant, dryRun }, assistantConfig);
|
|
446
|
+
const generate = runGenerate({ skipGenerate: Boolean(options.skipGenerate), dryRun });
|
|
447
|
+
|
|
263
448
|
const summary = {
|
|
264
449
|
assistant,
|
|
265
|
-
target,
|
|
266
|
-
targetSource,
|
|
450
|
+
target: guard.target,
|
|
451
|
+
targetSource: guard.targetSource,
|
|
267
452
|
dryRun,
|
|
268
|
-
forceScriptUpdate:
|
|
453
|
+
forceScriptUpdate: Boolean(options.forceScriptUpdate),
|
|
454
|
+
packageManager,
|
|
455
|
+
install,
|
|
456
|
+
scaffold,
|
|
457
|
+
generate,
|
|
269
458
|
packageJson: {
|
|
270
459
|
path: path.relative(cwd, packageJsonPath),
|
|
271
|
-
changed: packageResult.changed,
|
|
460
|
+
changed: guard.packageResult.changed,
|
|
272
461
|
scripts: {
|
|
273
|
-
'lint:sparkle': packageResult.packageJson.scripts?.['lint:sparkle'] ?? null,
|
|
274
|
-
'lint:sparkle:json': packageResult.packageJson.scripts?.['lint:sparkle:json'] ?? null,
|
|
462
|
+
'lint:sparkle': guard.packageResult.packageJson.scripts?.['lint:sparkle'] ?? null,
|
|
463
|
+
'lint:sparkle:json': guard.packageResult.packageJson.scripts?.['lint:sparkle:json'] ?? null,
|
|
275
464
|
},
|
|
276
|
-
results: packageResult.scriptResults,
|
|
465
|
+
results: guard.packageResult.scriptResults,
|
|
277
466
|
},
|
|
278
467
|
instructions: {
|
|
279
|
-
path: path.relative(cwd, instructionPath),
|
|
280
|
-
changed: instructionResult.changed,
|
|
281
|
-
existed: instructionResult.existed,
|
|
468
|
+
path: path.relative(cwd, guard.instructionPath),
|
|
469
|
+
changed: guard.instructionResult.changed,
|
|
470
|
+
existed: guard.instructionResult.existed,
|
|
282
471
|
},
|
|
283
472
|
};
|
|
284
473
|
|