sparkle-design-cli 2.0.7-rc.2 → 2.0.7-rc.4
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 +19 -10
- package/lib/anti-pattern-rules.js +8 -2
- package/lib/check.js +60 -16
- package/lib/setup.js +83 -17
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -24,11 +24,11 @@ npm install -g sparkle-design-cli
|
|
|
24
24
|
|
|
25
25
|
npm の dist-tag でチャネルを分けています。通常は `latest` を使い、RC や beta は先行試用時のみ指定してください。
|
|
26
26
|
|
|
27
|
-
| チャネル
|
|
28
|
-
|
|
|
29
|
-
| 安定版
|
|
30
|
-
| Release Candidate | `next`
|
|
31
|
-
| Beta
|
|
27
|
+
| チャネル | dist-tag | バージョン形式 | 用途 |
|
|
28
|
+
| ----------------- | -------- | -------------- | --------------------------- |
|
|
29
|
+
| 安定版 | `latest` | `X.Y.Z` | 本番運用向け(デフォルト) |
|
|
30
|
+
| Release Candidate | `next` | `X.Y.Z-rc.N` | GA 直前の検証、チーム展開前 |
|
|
31
|
+
| Beta | `beta` | `X.Y.Z-beta.N` | 品質保証中の検証用 |
|
|
32
32
|
|
|
33
33
|
```bash
|
|
34
34
|
# latest を使う(デフォルト)
|
|
@@ -203,7 +203,7 @@ npx --yes sparkle-design-cli setup --assistant claude --skip-install --skip-scaf
|
|
|
203
203
|
|
|
204
204
|
- `claude`: `CLAUDE.md`
|
|
205
205
|
- `codex`: `AGENTS.md`
|
|
206
|
-
- `cursor`: `.cursor/rules
|
|
206
|
+
- `cursor`: `AGENTS.md`(rc.4 から `.cursor/rules/*.mdc` を廃止。Cursor は AGENTS.md を always-load するため、条件付き読み込みで Guard が silent に skip される問題を解消)
|
|
207
207
|
- `generic`: `SPARKLE-DESIGN-AI.md`
|
|
208
208
|
|
|
209
209
|
#### setup オプション一覧
|
|
@@ -291,10 +291,19 @@ Next.js / Vite 以外ではアプリケーションフレームワークごと
|
|
|
291
291
|
```html
|
|
292
292
|
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
|
293
293
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
|
294
|
-
<link
|
|
294
|
+
<link
|
|
295
|
+
rel="stylesheet"
|
|
296
|
+
href="https://fonts.googleapis.com/css2?family=Material+Symbols+Rounded:FILL,wght@0..1,500&display=block"
|
|
297
|
+
/>
|
|
295
298
|
<!-- sparkle.config.json の font-pro / font-mono に合わせた Google Fonts URL -->
|
|
296
|
-
<link
|
|
297
|
-
|
|
299
|
+
<link
|
|
300
|
+
rel="stylesheet"
|
|
301
|
+
href="https://fonts.googleapis.com/css2?family=Inter:wght@400;700&display=swap"
|
|
302
|
+
/>
|
|
303
|
+
<link
|
|
304
|
+
rel="stylesheet"
|
|
305
|
+
href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;700&display=swap"
|
|
306
|
+
/>
|
|
298
307
|
```
|
|
299
308
|
|
|
300
309
|
**7. アンチパターン検査を package.json に追加(任意)**
|
|
@@ -310,7 +319,7 @@ Next.js / Vite 以外ではアプリケーションフレームワークごと
|
|
|
310
319
|
|
|
311
320
|
**8. AI エージェントを使うなら Guard ブロックと hook を手動で配置**
|
|
312
321
|
|
|
313
|
-
AI ガード(`CLAUDE.md` / `AGENTS.md
|
|
322
|
+
AI ガード(`CLAUDE.md` / `AGENTS.md`)と hook 設定(`.claude/settings.json` / `.cursor/hooks.json` / `.codex/hooks.json`)は `setup` に任せるのが最も楽です。Cursor 用 Guard は rc.4 から AGENTS.md に統一しました(`.cursor/rules/*.mdc` は条件付き読み込みのため)。
|
|
314
323
|
|
|
315
324
|
```bash
|
|
316
325
|
# ガード追加・hook 設定のみ(パッケージインストールや scaffold はスキップ)
|
|
@@ -925,7 +925,7 @@ const ANTI_PATTERN_GROUPS = [
|
|
|
925
925
|
check: {
|
|
926
926
|
description: 'Tailwind デフォルト typography を Sparkle Design コンポーネント内で使わない',
|
|
927
927
|
recommendation:
|
|
928
|
-
'text-sm / text-xs / text-base / font-medium / font-semibold / font-bold は character-* utility
|
|
928
|
+
'text-sm / text-xs / text-base / font-medium / font-semibold / font-bold は character-* utility に置き換えてください。ただし character-* に対応する token が無いサイズ(text-[10px] 等の arbitrary value、あるいは意図的に token 外のサイズを使う場合)は、同一行または直前行に `// sparkle-disable-line tailwind-typography` コメントを付けて例外扱いとして残すこともできます。',
|
|
929
929
|
pattern: /\b(text-(?:xs|sm|base|lg|xl|2xl)|font-(?:medium|semibold|bold|normal|light))\b/g,
|
|
930
930
|
},
|
|
931
931
|
featureSection: lines([
|
|
@@ -937,9 +937,15 @@ const ANTI_PATTERN_GROUPS = [
|
|
|
937
937
|
'',
|
|
938
938
|
'// ❌ Wrong — Tailwind デフォルトの typography を使わない',
|
|
939
939
|
'<span className="text-sm font-medium">テキスト</span>',
|
|
940
|
+
'',
|
|
941
|
+
'// 例外 — character-* に対応するサイズが無い場合は arbitrary value を使うか',
|
|
942
|
+
'// suppress コメントを付けて残す',
|
|
943
|
+
'<span className="text-[10px] text-text-low">12px 未満の極小メタ情報</span>',
|
|
944
|
+
'{/* sparkle-disable-next-line tailwind-typography */}',
|
|
945
|
+
'<span className="text-xs">どうしても text-xs で残したいケース</span>',
|
|
940
946
|
'```',
|
|
941
947
|
'',
|
|
942
|
-
'Sparkle Design コンポーネント内では `character-*-pro` / `character-*-mono` を使用する。Tailwind の `text-
|
|
948
|
+
'Sparkle Design コンポーネント内では `character-*-pro` / `character-*-mono` を使用する。character-1(12px)より小さい指定や、対応 token が無いサイズは Tailwind の arbitrary value (`text-[10px]` 等) で表現するか、`// sparkle-disable-line tailwind-typography` で個別に例外指定する。',
|
|
943
949
|
]),
|
|
944
950
|
jsdocTargets: [],
|
|
945
951
|
},
|
package/lib/check.js
CHANGED
|
@@ -85,8 +85,66 @@ function formatSnippet(text) {
|
|
|
85
85
|
return text.replace(/\s+/g, ' ').trim().slice(0, 120);
|
|
86
86
|
}
|
|
87
87
|
|
|
88
|
+
/**
|
|
89
|
+
* ESLint 風の suppression コメントをサポートする。以下のいずれかが findings
|
|
90
|
+
* と同じ行、または直前行にあれば該当 rule の finding は emit しない:
|
|
91
|
+
*
|
|
92
|
+
* // sparkle-disable-line <rule-id>
|
|
93
|
+
* // sparkle-disable-next-line <rule-id>
|
|
94
|
+
* {/* sparkle-disable-line <rule-id> *\/} (JSX コメント)
|
|
95
|
+
*
|
|
96
|
+
* ユーザーがどうしても character-* に移行できない typography(token に
|
|
97
|
+
* 存在しないサイズなど)や、特定 file だけ例外扱いしたい場合の escape
|
|
98
|
+
* hatch として用意する。ルール ID は複数カンマ区切り可(例:
|
|
99
|
+
* `sparkle-disable-line tailwind-typography, card-padding-override`)。
|
|
100
|
+
*
|
|
101
|
+
* en: ESLint-style suppression comments so specific findings can be opted
|
|
102
|
+
* out when character-* / Sparkle tokens don't map cleanly (e.g. sub-token
|
|
103
|
+
* font sizes). Same-line or previous-line comment is honored.
|
|
104
|
+
*/
|
|
105
|
+
const SUPPRESS_LINE = /(?:\/\/|\/\*|\{\/\*)\s*sparkle-disable-line\s+([A-Za-z0-9,\s_-]+?)\s*(?:\*\/\}?|$)/;
|
|
106
|
+
const SUPPRESS_NEXT_LINE =
|
|
107
|
+
/(?:\/\/|\/\*|\{\/\*)\s*sparkle-disable-next-line\s+([A-Za-z0-9,\s_-]+?)\s*(?:\*\/\}?|$)/;
|
|
108
|
+
|
|
109
|
+
function extractSuppressedRules(source, regex) {
|
|
110
|
+
if (!source) return null;
|
|
111
|
+
const m = source.match(regex);
|
|
112
|
+
if (!m) return null;
|
|
113
|
+
return new Set(
|
|
114
|
+
m[1]
|
|
115
|
+
.split(',')
|
|
116
|
+
.map((id) => id.trim())
|
|
117
|
+
.filter(Boolean)
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function isSuppressed(ruleId, contentLines, lineNumber) {
|
|
122
|
+
// lineNumber は 1-origin
|
|
123
|
+
// en: 1-based line numbers from getLineNumber.
|
|
124
|
+
const current = contentLines[lineNumber - 1] ?? '';
|
|
125
|
+
const previous = contentLines[lineNumber - 2] ?? '';
|
|
126
|
+
const sameLine = extractSuppressedRules(current, SUPPRESS_LINE);
|
|
127
|
+
if (sameLine && sameLine.has(ruleId)) return true;
|
|
128
|
+
const prevLine = extractSuppressedRules(previous, SUPPRESS_NEXT_LINE);
|
|
129
|
+
if (prevLine && prevLine.has(ruleId)) return true;
|
|
130
|
+
return false;
|
|
131
|
+
}
|
|
132
|
+
|
|
88
133
|
function collectFindings(filePath, content) {
|
|
89
134
|
const findings = [];
|
|
135
|
+
const contentLines = content.split(/\r?\n/);
|
|
136
|
+
const pushFinding = (rule, index, snippet) => {
|
|
137
|
+
const line = getLineNumber(content, index ?? 0);
|
|
138
|
+
if (isSuppressed(rule.id, contentLines, line)) return;
|
|
139
|
+
findings.push({
|
|
140
|
+
filePath,
|
|
141
|
+
id: rule.id,
|
|
142
|
+
description: rule.description,
|
|
143
|
+
recommendation: rule.recommendation,
|
|
144
|
+
line,
|
|
145
|
+
snippet,
|
|
146
|
+
});
|
|
147
|
+
};
|
|
90
148
|
|
|
91
149
|
for (const rule of RULES) {
|
|
92
150
|
if (typeof rule.match === 'function') {
|
|
@@ -94,26 +152,12 @@ function collectFindings(filePath, content) {
|
|
|
94
152
|
// 複雑な照合が必要なルール向けの opt-in API。2-pass 方式などで regex 単体の
|
|
95
153
|
// backtracking リスクを回避したいときに使う。index は content 内の絶対オフセット。
|
|
96
154
|
for (const hit of rule.match(content)) {
|
|
97
|
-
|
|
98
|
-
filePath,
|
|
99
|
-
id: rule.id,
|
|
100
|
-
description: rule.description,
|
|
101
|
-
recommendation: rule.recommendation,
|
|
102
|
-
line: getLineNumber(content, hit.index ?? 0),
|
|
103
|
-
snippet: formatSnippet(hit.text ?? ''),
|
|
104
|
-
});
|
|
155
|
+
pushFinding(rule, hit.index ?? 0, formatSnippet(hit.text ?? ''));
|
|
105
156
|
}
|
|
106
157
|
continue;
|
|
107
158
|
}
|
|
108
159
|
for (const match of content.matchAll(rule.pattern)) {
|
|
109
|
-
|
|
110
|
-
filePath,
|
|
111
|
-
id: rule.id,
|
|
112
|
-
description: rule.description,
|
|
113
|
-
recommendation: rule.recommendation,
|
|
114
|
-
line: getLineNumber(content, match.index ?? 0),
|
|
115
|
-
snippet: formatMatch(match),
|
|
116
|
-
});
|
|
160
|
+
pushFinding(rule, match.index ?? 0, formatMatch(match));
|
|
117
161
|
}
|
|
118
162
|
}
|
|
119
163
|
|
package/lib/setup.js
CHANGED
|
@@ -56,6 +56,15 @@ const config = {
|
|
|
56
56
|
export default config;
|
|
57
57
|
`;
|
|
58
58
|
|
|
59
|
+
// Guard ブロックは「常に読まれる」ファイルだけに置く。Cursor は 2025 年後半
|
|
60
|
+
// 以降 AGENTS.md を公式サポートしている(https://agents.md)ので、
|
|
61
|
+
// `.cursor/rules/*.mdc`(frontmatter で `alwaysApply: true` を付けない限り
|
|
62
|
+
// 条件付き読み込み)は使わない。条件付き読み込みは Guard が読まれない
|
|
63
|
+
// セッションで silent に lint:sparkle 実行漏れを起こす。
|
|
64
|
+
// en: Keep Guard on always-loaded files. `.cursor/rules/*.mdc` is loaded
|
|
65
|
+
// conditionally unless `alwaysApply: true` is set in frontmatter, which
|
|
66
|
+
// silently skips the Guard in some sessions. Cursor (Codex / Aider / Zed
|
|
67
|
+
// / Gemini CLI etc.) all read AGENTS.md, so route Cursor through it.
|
|
59
68
|
const ASSISTANT_CONFIG = {
|
|
60
69
|
claude: {
|
|
61
70
|
path: 'CLAUDE.md',
|
|
@@ -64,13 +73,20 @@ const ASSISTANT_CONFIG = {
|
|
|
64
73
|
path: 'AGENTS.md',
|
|
65
74
|
},
|
|
66
75
|
cursor: {
|
|
67
|
-
path: '.
|
|
76
|
+
path: 'AGENTS.md',
|
|
68
77
|
},
|
|
69
78
|
generic: {
|
|
70
79
|
path: 'SPARKLE-DESIGN-AI.md',
|
|
71
80
|
},
|
|
72
81
|
};
|
|
73
82
|
|
|
83
|
+
// 旧版(rc.3 以前)が cursor 用に書いていた `.mdc` パス。残骸を検出して
|
|
84
|
+
// AGENTS.md への移行を促す warn に使う。silent 削除はしない(ユーザーが
|
|
85
|
+
// frontmatter を付けて意図的に保持しているケースもあるため)。
|
|
86
|
+
// en: Path used by rc.3 and earlier for cursor assistant. We keep it as a
|
|
87
|
+
// constant so the migration warn can detect leftovers without re-typing.
|
|
88
|
+
const LEGACY_CURSOR_GUARD_PATH = '.cursor/rules/sparkle-design-guard.mdc';
|
|
89
|
+
|
|
74
90
|
const BLOCK_START = '<!-- sparkle-design-cli:setup:start -->';
|
|
75
91
|
const BLOCK_END = '<!-- sparkle-design-cli:setup:end -->';
|
|
76
92
|
const TARGET_CANDIDATES = ['src', 'src/components', 'src/features', 'app', 'components'];
|
|
@@ -206,8 +222,14 @@ function isManagedSparkleScript(value, mode) {
|
|
|
206
222
|
return false;
|
|
207
223
|
}
|
|
208
224
|
|
|
209
|
-
function buildInstructionBlock(target,
|
|
210
|
-
|
|
225
|
+
function buildInstructionBlock(target, _assistant) {
|
|
226
|
+
// Guard ブロックは常に h2 で出す。rc.3 以前は cursor 用に独立 .mdc ファイル
|
|
227
|
+
// へ書いていたため h1 にしていたが、rc.4 で AGENTS.md に統一したため、
|
|
228
|
+
// 親ファイルの heading 階層に揃えて h2 が常に正しい。
|
|
229
|
+
// en: Always emit Guard as h2. rc.3 used h1 for the standalone cursor
|
|
230
|
+
// .mdc file, but rc.4 routes cursor through AGENTS.md so h2 fits the
|
|
231
|
+
// surrounding section hierarchy.
|
|
232
|
+
const heading = '## Sparkle Design Guard';
|
|
211
233
|
|
|
212
234
|
return [
|
|
213
235
|
BLOCK_START,
|
|
@@ -220,7 +242,7 @@ function buildInstructionBlock(target, assistant) {
|
|
|
220
242
|
' - Sparkle fonts (Google Fonts preconnect + Material Symbols + the configured pro/mono fonts) must be present in the document `<head>`: React layouts via `<SparkleHead />` placed inside `<head>` in the root layout, Vite projects via the managed `<!-- sparkle-design-cli:fonts:start -->` … `end` block in `index.html`.',
|
|
221
243
|
' - Do **not** hand-define `--color-primary-*` or other Sparkle tokens as fallbacks when they look missing. Those come from `sparkle-design.css`; missing values mean the `@import` path is wrong (e.g. `./sparkle-design.css` used when the file lives under `./app/`). Fix the import path, do not duplicate the tokens.',
|
|
222
244
|
' - If you detect any of the above drifted, re-run `npx --yes sparkle-design-cli generate` before hand-editing. The CLI restores the canonical state (correct relative paths, `@source` placement, `index.html` injection).',
|
|
223
|
-
'- **Required**: After creating or modifying any UI component, run `lint:sparkle` before finishing. This catches Sparkle Design anti-patterns.',
|
|
245
|
+
'- **Required**: After creating or modifying any UI component, run `lint:sparkle` before finishing. This catches Sparkle Design anti-patterns. To opt out a specific line from a rule when the Sparkle token does not map cleanly (e.g. sub-12px font sizes for `tailwind-typography`), add `// sparkle-disable-line <rule-id>` on the same line or `// sparkle-disable-next-line <rule-id>` on the line above — but use it sparingly and only when the migration would actually break the design.',
|
|
224
246
|
`- For AI review, prefer \`lint:sparkle:json\` or run \`npx --yes sparkle-design-cli check ${target} --format json\` directly.`,
|
|
225
247
|
'- You must inspect every entry in both `findings` and `manualReviewReminders`. For each entry in `manualReviewReminders`, **echo the reminder ID in your final reply together with a short review note** (e.g. `[badge-tag-semantics] reviewed — Badge is used only for counts, so the usage is fine.`). Silence on a reminder means it was skipped; always state at least a one-line judgment per ID. This is not blocked by the Stop hook (reminders are judgment calls), but reviewers / humans rely on the explicit acknowledgment to trust that each item was actually considered.',
|
|
226
248
|
BLOCK_END,
|
|
@@ -243,10 +265,7 @@ function updateInstructionFile(filePath, block) {
|
|
|
243
265
|
`⚠️ ${filePath} に Sparkle Design Guard ブロックが ${markerCount} 個見つかりました。すべて最新版で置き換えます(手動でコピーした古い block が残っている可能性があります)。`
|
|
244
266
|
);
|
|
245
267
|
}
|
|
246
|
-
const next = current.replace(
|
|
247
|
-
new RegExp(`${BLOCK_START}[\\s\\S]*?${BLOCK_END}`, 'g'),
|
|
248
|
-
block
|
|
249
|
-
);
|
|
268
|
+
const next = current.replace(new RegExp(`${BLOCK_START}[\\s\\S]*?${BLOCK_END}`, 'g'), block);
|
|
250
269
|
return { changed: next !== current, content: next, existed: exists };
|
|
251
270
|
}
|
|
252
271
|
|
|
@@ -510,6 +529,42 @@ function runAssistantGuard(cwd, packageJsonPath, options, assistantConfig) {
|
|
|
510
529
|
}
|
|
511
530
|
}
|
|
512
531
|
|
|
532
|
+
// rc.4 で Cursor の Guard 出力先を `.cursor/rules/sparkle-design-guard.mdc`
|
|
533
|
+
// から AGENTS.md に移したため、再 setup 時に旧 .mdc が残っているとガードが
|
|
534
|
+
// 二重定義になる(しかも .mdc は frontmatter 次第で読まれない場合がある)。
|
|
535
|
+
// silent 削除はせず、警告だけ出して移行判断はユーザーに委ねる。Cursor 以外
|
|
536
|
+
// のアシスタントでも、過去に cursor → 別 assistant に切り替えたユーザーが
|
|
537
|
+
// いるので assistant 値に関係なくチェックする。
|
|
538
|
+
// en: rc.4 moved Cursor Guard from `.cursor/rules/*.mdc` to AGENTS.md.
|
|
539
|
+
// Detect leftovers and warn the user instead of deleting silently — the
|
|
540
|
+
// .mdc may still be intentionally retained with `alwaysApply: true`.
|
|
541
|
+
let legacyCursorGuard = null;
|
|
542
|
+
if (!options.instructionsPath) {
|
|
543
|
+
const legacyPath = path.resolve(cwd, LEGACY_CURSOR_GUARD_PATH);
|
|
544
|
+
if (fs.existsSync(legacyPath)) {
|
|
545
|
+
try {
|
|
546
|
+
const content = fs.readFileSync(legacyPath, 'utf8');
|
|
547
|
+
// BLOCK_START だけでなく BLOCK_END も含み、かつ START が END より
|
|
548
|
+
// 前にある完全な block の場合のみ legacy 判定する。途中で削れた断片や
|
|
549
|
+
// ユーザーが手動でコピーした start マーカーだけ残っているケースを
|
|
550
|
+
// legacy 警告対象から外す(false positive 防止)。
|
|
551
|
+
// en: Require both markers in the correct order so partial fragments
|
|
552
|
+
// don't trigger the migration warn.
|
|
553
|
+
const startIndex = content.indexOf(BLOCK_START);
|
|
554
|
+
const endIndex = content.indexOf(BLOCK_END);
|
|
555
|
+
if (startIndex !== -1 && endIndex !== -1 && startIndex < endIndex) {
|
|
556
|
+
legacyCursorGuard = { path: legacyPath };
|
|
557
|
+
}
|
|
558
|
+
} catch (error) {
|
|
559
|
+
// 読み込み失敗(権限等)は legacy 検知だけスキップ。setup 本体は続行。
|
|
560
|
+
// en: Skip legacy detection on read failure; do not block setup.
|
|
561
|
+
console.warn(
|
|
562
|
+
`⚠️ ${LEGACY_CURSOR_GUARD_PATH} の読み込みに失敗したため legacy ガード検出をスキップします (${error.code ?? error.message})`
|
|
563
|
+
);
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
|
|
513
568
|
if (!options.dryRun) {
|
|
514
569
|
if (packageResult.changed) writeJson(packageJsonPath, packageResult.packageJson);
|
|
515
570
|
if (instructionResult.changed) {
|
|
@@ -530,6 +585,7 @@ function runAssistantGuard(cwd, packageJsonPath, options, assistantConfig) {
|
|
|
530
585
|
instructionResult,
|
|
531
586
|
agentsInstructionPath,
|
|
532
587
|
agentsInstructionResult,
|
|
588
|
+
legacyCursorGuard,
|
|
533
589
|
};
|
|
534
590
|
}
|
|
535
591
|
|
|
@@ -624,12 +680,9 @@ function runClaudeHook(cwd, target, dryRun) {
|
|
|
624
680
|
const managedCommand = buildManagedHookCommand(target);
|
|
625
681
|
const { existed, config } = loadHookJson(settingsPath, '.claude/settings.json');
|
|
626
682
|
|
|
627
|
-
const nextSettings =
|
|
628
|
-
typeof config === 'object' && config ? { ...config } : {};
|
|
683
|
+
const nextSettings = typeof config === 'object' && config ? { ...config } : {};
|
|
629
684
|
const hooks =
|
|
630
|
-
typeof nextSettings.hooks === 'object' && nextSettings.hooks
|
|
631
|
-
? { ...nextSettings.hooks }
|
|
632
|
-
: {};
|
|
685
|
+
typeof nextSettings.hooks === 'object' && nextSettings.hooks ? { ...nextSettings.hooks } : {};
|
|
633
686
|
const stopGroups = Array.isArray(hooks.Stop) ? [...hooks.Stop] : [];
|
|
634
687
|
|
|
635
688
|
const alreadyPresent = stopGroups.some(
|
|
@@ -908,6 +961,11 @@ export function setupAssistant(options = {}) {
|
|
|
908
961
|
existed: guard.agentsInstructionResult?.existed ?? false,
|
|
909
962
|
}
|
|
910
963
|
: null,
|
|
964
|
+
legacyCursorGuard: guard.legacyCursorGuard
|
|
965
|
+
? {
|
|
966
|
+
path: path.relative(cwd, guard.legacyCursorGuard.path),
|
|
967
|
+
}
|
|
968
|
+
: null,
|
|
911
969
|
hook: hook
|
|
912
970
|
? {
|
|
913
971
|
assistant: hook.assistant,
|
|
@@ -924,8 +982,7 @@ export function setupAssistant(options = {}) {
|
|
|
924
982
|
sessionRootMismatch: hook.sessionRootMismatch
|
|
925
983
|
? {
|
|
926
984
|
detected: true,
|
|
927
|
-
sessionDir: path.relative(cwd, hook.sessionRootMismatch.sessionDir) ||
|
|
928
|
-
'.',
|
|
985
|
+
sessionDir: path.relative(cwd, hook.sessionRootMismatch.sessionDir) || '.',
|
|
929
986
|
}
|
|
930
987
|
: null,
|
|
931
988
|
}
|
|
@@ -937,7 +994,10 @@ export function setupAssistant(options = {}) {
|
|
|
937
994
|
// セットアップ後のリマインダー。AI の会話履歴にも残るよう stderr に出す。
|
|
938
995
|
// en: Post-setup reminder. Written to stderr so it stays in the AI transcript
|
|
939
996
|
// without polluting the JSON stdout payload that tooling parses.
|
|
940
|
-
printPostSetupReminder(guard.target, packageManager, {
|
|
997
|
+
printPostSetupReminder(guard.target, packageManager, {
|
|
998
|
+
hook,
|
|
999
|
+
legacyCursorGuard: guard.legacyCursorGuard,
|
|
1000
|
+
});
|
|
941
1001
|
|
|
942
1002
|
return summary;
|
|
943
1003
|
}
|
|
@@ -965,7 +1025,7 @@ const HOOK_LABELS = {
|
|
|
965
1025
|
codex: { name: 'Codex', file: '.codex/hooks.json', event: 'Stop' },
|
|
966
1026
|
};
|
|
967
1027
|
|
|
968
|
-
function printPostSetupReminder(target, packageManager, { hook } = {}) {
|
|
1028
|
+
function printPostSetupReminder(target, packageManager, { hook, legacyCursorGuard } = {}) {
|
|
969
1029
|
const lintTarget = target || 'src';
|
|
970
1030
|
const lintCmd = buildLintCommand(packageManager);
|
|
971
1031
|
const lines = [
|
|
@@ -1001,6 +1061,12 @@ function printPostSetupReminder(target, packageManager, { hook } = {}) {
|
|
|
1001
1061
|
);
|
|
1002
1062
|
}
|
|
1003
1063
|
}
|
|
1064
|
+
if (legacyCursorGuard) {
|
|
1065
|
+
lines.push(
|
|
1066
|
+
` ⚠️ ${legacyCursorGuard.path} に旧バージョンの Sparkle Design Guard ブロックが残っています。rc.4 で Cursor のガードは AGENTS.md に統一されました(Cursor の \`.cursor/rules/*.mdc\` は frontmatter で \`alwaysApply: true\` を付けないと条件付き読み込みになり、ガードが silent に skip される問題があるため)。重複防止のため旧 .mdc を削除するか、\`alwaysApply: true\` の frontmatter を付けて意図的に残す運用に切り替えてください。`,
|
|
1067
|
+
` ⚠️ Found leftover Sparkle Design Guard in ${legacyCursorGuard.path}. rc.4 routes Cursor Guard through AGENTS.md (\`.cursor/rules/*.mdc\` is conditionally loaded unless \`alwaysApply: true\` is set). Either delete the legacy .mdc or add \`alwaysApply: true\` frontmatter to keep it deliberately.`
|
|
1068
|
+
);
|
|
1069
|
+
}
|
|
1004
1070
|
lines.push('');
|
|
1005
1071
|
for (const line of lines) {
|
|
1006
1072
|
console.error(line);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sparkle-design-cli",
|
|
3
|
-
"version": "2.0.7-rc.
|
|
3
|
+
"version": "2.0.7-rc.4",
|
|
4
4
|
"description": "Sparkle Design CLI — プロジェクトセットアップ、CSS・フォント生成、アンチパターン検査、AI エージェント(Claude Code / Cursor / Codex)向けのガードと hook 設定まで一括で行う sparkle-design 公式 CLI。",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"registry": "https://registry.npmjs.org",
|