sparkle-design-cli 1.6.2 → 2.0.1

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 CHANGED
@@ -1,6 +1,27 @@
1
1
  # Sparkle Design CLI
2
2
 
3
- Sparkle Design の CSS 生成、導入先プロジェクト向けのアンチパターン検査、AI エージェント向け guard 設定を行う CLI ツールです。
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: AI ガード設定の差し込み
167
+ ### setup: プロジェクトのフルセットアップ
168
+
169
+ `sparkle-design-cli setup` は、Sparkle Design の導入に必要な作業をまとめて行います:
145
170
 
146
- `sparkle-design-cli setup` は、導入先プロジェクトの `package.json` と AI 向け指示ファイルに Sparkle Design guard を追加します。
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
- # generic な Markdown を生成
159
- npx --yes sparkle-design-cli setup --assistant generic
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
- - `package.json` の `lint:sparkle`
164
- - `package.json` の `lint:sparkle:json`
165
- - assistant ごとの指示ファイルへの managed block
191
+ 既存ファイルは上書きされません:
192
+ - `sparkle.config.json` / `postcss.config.*` / `globals.css` が存在する場合はそのまま保持
193
+ - 既に依存関係に入っているパッケージはインストールをスキップ
194
+ - `package.json` の既存の `lint:sparkle*` 独自 script は保持(`--force-script-update` で上書き)
166
195
 
167
- `--target` を省略した場合は `src`, `src/components`, `src/features`, `app`, `components` の順で自動検出します。既存の `lint:sparkle*` がすでに Sparkle Design 向け script なら再実行時に更新されます。独自 script は保持され、上書きしたい場合だけ `--force-script-update` を付けます。
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
- `@goodpatch/sparkle-design` を npm パッケージとして利用する場合に必須。`globals.css` に `@source` ディレクティブを自動挿入します。`@goodpatch/sparkle-design` は常にデフォルトで含まれます。
281
+ `sparkle-design` を npm パッケージとして利用する場合に必須。`globals.css` に `@source` ディレクティブを自動挿入します。`sparkle-design` は常にデフォルトで含まれます。
242
282
 
243
283
  #### extend.custom-css
244
284
 
@@ -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 エージェント向けの guard 設定を導入
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 --assistant claude
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/constants.js CHANGED
@@ -65,8 +65,15 @@ export const FONT_DOMAINS = {
65
65
  // フォントのデフォルト設定
66
66
  export const FONT_DEFAULTS = {
67
67
  WEIGHTS: [400, 700],
68
+ // Material Symbols は variable font だが、sparkle-design.css では
69
+ // wght=500 / opsz=24 / GRAD=0 固定で FILL のみ 0/1 を切り替えている。
70
+ // 補間用のレンジを落として woff2 サイズを大幅に削減するため、軸指定は
71
+ // FILL: 0..1 / wght: 500(単一値)に絞る。opsz / GRAD は単一値のため省略。
72
+ // en: Narrow the axis range requested from Google Fonts. Since the emitted CSS
73
+ // only varies FILL (0/1) and uses fixed wght=500, opsz=24, GRAD=0, we can
74
+ // drop the wght/opsz/GRAD ranges. This reduces the woff2 payload by ~60-75%.
68
75
  MATERIAL_SYMBOLS_IMPORT:
69
- "@import 'https://fonts.googleapis.com/css2?family=Material+Symbols+Rounded:opsz,wght,FILL,GRAD@20..48,100..700,0..1,0..200&display=block';",
76
+ "@import 'https://fonts.googleapis.com/css2?family=Material+Symbols+Rounded:FILL,wght@0..1,500&display=block';",
70
77
  GENERIC_FAMILY: {
71
78
  pro: 'sans-serif',
72
79
  mono: 'monospace',
@@ -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 = '@goodpatch/sparkle-design';
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
- export function setupAssistant(options = {}) {
225
- const assistant = options.assistant ?? 'generic';
226
- const cwd = process.cwd();
227
- const dryRun = Boolean(options.dryRun);
228
- const force = Boolean(options.forceScriptUpdate);
229
- const assistantConfig = ASSISTANT_CONFIG[assistant];
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 (!assistantConfig) {
232
- throw new Error(`Unsupported assistant: ${assistant}`);
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 targetSource = options.target ? 'explicit' : 'auto';
236
- const target = options.target
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
- const packageJsonPath = path.resolve(cwd, 'package.json');
241
- if (!fs.existsSync(packageJsonPath)) {
242
- throw new Error(`package.json not found in ${cwd}`);
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: force,
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
 
package/package.json CHANGED
@@ -1,7 +1,11 @@
1
1
  {
2
2
  "name": "sparkle-design-cli",
3
- "version": "1.6.2",
3
+ "version": "2.0.1",
4
4
  "description": "Sparkle Design CSS Generator - デザインシステムCSSを設定ファイルから生成するツール",
5
+ "publishConfig": {
6
+ "registry": "https://registry.npmjs.org",
7
+ "access": "public"
8
+ },
5
9
  "main": "lib/generate-css.js",
6
10
  "type": "module",
7
11
  "bin": {