@42ws/cli 0.0.49 → 0.1.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.
@@ -0,0 +1,242 @@
1
+ // AI に渡す「素材+抽出指示」プロンプトを生成する。
2
+ // `forms import` / `collections import` はサーバ側 AI を使わず、ここで作った
3
+ // プロンプトを stdout に出して、呼び出し元の AI(Claude Code / Codex 等)に
4
+ // JSON を生成させ、`forms create` / `collections create` で書き戻す。
5
+ function quoteShell(s) {
6
+ if (/^[a-zA-Z0-9_\-./@#:%]+$/.test(s))
7
+ return s;
8
+ return `'${s.replace(/'/g, `'\\''`)}'`;
9
+ }
10
+ function buildCreateCmd(parts) {
11
+ const args = [];
12
+ for (const [k, v] of Object.entries(parts)) {
13
+ if (v === undefined || v === false || v === '')
14
+ continue;
15
+ if (v === true)
16
+ args.push(`--${k}`);
17
+ else
18
+ args.push(`--${k} ${quoteShell(String(v))}`);
19
+ }
20
+ return args.join(' ');
21
+ }
22
+ export function buildFormsImportPrompt(input) {
23
+ const { html, source, notify, name, inject } = input;
24
+ const baseUrl = input.baseUrl ?? (source.kind === 'url' ? source.url : '');
25
+ const linkArgs = source.kind === 'site'
26
+ ? { site: source.site, page: source.page }
27
+ : source.kind === 'file'
28
+ ? { file: source.path }
29
+ : {};
30
+ const createCmdTail = buildCreateCmd({
31
+ 'from-json': '<JSON 出力ファイル>',
32
+ notify,
33
+ name,
34
+ ...linkArgs,
35
+ inject,
36
+ });
37
+ return `# フォーム取り込みタスク
38
+
39
+ 42ws の \`forms import\` は AI 側(あなた)でフォーム定義を抽出する設計です。
40
+ 以下の HTML を読み取り、フォーム定義 JSON を作って一時ファイルに保存し、最後に \`forms create\` で書き戻してください。
41
+
42
+ ## 取り込み元
43
+
44
+ - 種別: ${source.kind}${source.kind === 'site' ? ` (${source.site}/${source.page})` : source.kind === 'url' ? ` (${source.url})` : ` (${source.path})`}
45
+ ${baseUrl ? `- baseUrl: ${baseUrl}` : ''}
46
+
47
+ ## 抽出ルール
48
+
49
+ - input / select / textarea を抽出。submit / button / hidden / honeypot (display:none 等) は除外
50
+ - ラベルは <label> / 前後の見出し / placeholder から推定。日本語ページなら日本語で
51
+ - type は text / email / tel / date / textarea / select / radio / checkbox / address のいずれか
52
+ - 「プライバシーポリシーに同意」等の同意必須チェックは type=checkbox + required=true。label を短い項目名、placeholder にチェックボックス横の文言全文
53
+ - 住所(郵便番号・都道府県・市区町村が並ぶ塊)は必ず単一の address フィールドにまとめる(郵便番号 → 住所の自動補完を持つため 3 つに割らない)
54
+ - select / radio / checkbox の options に選択肢の表示テキストを配列で
55
+ - required は required 属性 / aria-required / 「必須」表記から判定
56
+ - フォームのタイトル (h1/h2/legend 等) を name として返す
57
+ - 抽出箇所の note / 複数フォーム / ハニーポット除外などは warnings に短く記載
58
+ - 自信のない判断は confidence: "low"、明確なら "high"
59
+
60
+ ## 見た目の継承(重要)
61
+
62
+ - themeColor (#RRGGBB): 送信ボタン / CTA / ヘッダ / リンクなどページの主要アクセント色を 1 つ。CSS 変数や button/cta 系の background から推定。手がかりが無い場合のみ空文字
63
+ - css: 以下のクラス名にだけセレクタを当てて書く(<style> タグは不要、@import 禁止、8KB 以内)
64
+
65
+ 入力フォーム画面:
66
+ .contakun-form / .contakun-label / .contakun-input / .contakun-textarea
67
+ .contakun-select / .contakun-button-primary / .contakun-button-secondary
68
+ 確認・完了画面(忘れずに書く。雰囲気の一貫性のため):
69
+ .contakun-confirm / .contakun-confirm-title / .contakun-confirm-description
70
+ .contakun-confirm-content / .contakun-confirm-item / .contakun-confirm-label / .contakun-confirm-value
71
+ .contakun-complete / .contakun-complete-title / .contakun-complete-message
72
+
73
+ - 元ページの border-radius / padding / font / shadow をミミックする
74
+ - font-family は body や :root の指定をそのまま流用してよい(Web font の新規追加はしない)
75
+ - 役割ごとに差を付ける。同じ宣言を全クラスに当てない
76
+ - 完全に手がかりが無いときのみ css は空文字
77
+
78
+ ## 出力 JSON スキーマ
79
+
80
+ \`\`\`json
81
+ {
82
+ "name": "string",
83
+ "themeColor": "#RRGGBB | \\"\\"",
84
+ "css": "string | \\"\\"",
85
+ "warnings": ["string", ...],
86
+ "fields": [
87
+ {
88
+ "type": "text|email|tel|date|textarea|select|radio|checkbox|address",
89
+ "name": "英数字の name 属性",
90
+ "label": "画面表示ラベル",
91
+ "placeholder": "string?",
92
+ "required": true,
93
+ "options": ["string", ...]?,
94
+ "confidence": "high|low",
95
+ "note": "low の場合のみ推定根拠"
96
+ }
97
+ ]
98
+ }
99
+ \`\`\`
100
+
101
+ 抽出できない(フォームが見当たらない / 解釈不能)なら、その旨を簡潔に説明してユーザーに HTML 範囲の指定を促してください(JSON は出力しない)。
102
+
103
+ ## 完了したら
104
+
105
+ 1. 上記 JSON を一時ファイル(例: \`/tmp/42ws-form.json\`)に書き出す
106
+ 2. 次のコマンドで 42ws に登録:
107
+
108
+ \`\`\`
109
+ 42ws forms create ${createCmdTail}
110
+ \`\`\`
111
+
112
+ ${inject ? '`--inject` 付きなので、`forms create` が元ソースの最初の <form> を embed スニペットに置き換えて再公開します。\n' : ''}
113
+ ---
114
+
115
+ ## HTML
116
+
117
+ \`\`\`html
118
+ ${html}
119
+ \`\`\`
120
+ `;
121
+ }
122
+ export function buildCollectionsImportPrompt(input) {
123
+ const { html, source, name, slug, inject } = input;
124
+ const baseUrl = input.baseUrl ?? (source.kind === 'url' ? source.url : '');
125
+ const linkArgs = source.kind === 'site'
126
+ ? { site: source.site, page: source.page }
127
+ : source.kind === 'file'
128
+ ? { file: source.path }
129
+ : {};
130
+ const createCmdTail = buildCreateCmd({
131
+ 'from-json': '<JSON 出力ファイル>',
132
+ name,
133
+ slug,
134
+ ...linkArgs,
135
+ inject,
136
+ });
137
+ return `# コレクション取り込みタスク
138
+
139
+ 42ws の \`collections import\` は AI 側(あなた)で索引・詳細テンプレートを生成する設計です。
140
+ 以下の HTML を読み取り、コレクション定義 JSON を作って一時ファイルに保存し、最後に \`collections create\` で書き戻してください。
141
+
142
+ ## 取り込み元
143
+
144
+ - 種別: ${source.kind}${source.kind === 'site' ? ` (${source.site}/${source.page})` : source.kind === 'url' ? ` (${source.url})` : ` (${source.path})`}
145
+ ${baseUrl ? `- baseUrl: ${baseUrl}` : ''}
146
+
147
+ ## やること
148
+
149
+ - HTML からそのサイトの「雰囲気」(配色・余白・フォント・角丸・ボタン形・カード形・リンク色)を読み取り、CMS の **索引テンプレート (indexTemplate)** と **詳細テンプレート (detailTemplate)** を Mustache で生成する
150
+ - 索引は既存ページの \`<main>\` 等に埋め込まれる前提。**ヘッダー・フッター・グローバルナビは含めない**
151
+ - 元ページに記事一覧がある場合はその構造を尊重。なければ一般的なカード/ボタン/見出しスタイルから推定
152
+ - それぞれ \`<style>...</style>\` + HTML のフラグメントとして返す
153
+
154
+ ## themeColor (#RRGGBB)
155
+
156
+ - ページの主要アクセント色を 1 つ。CSS 変数や button/link の色から推定。完全に手がかりが無いときのみ空文字
157
+
158
+ ## Mustache 変数(使えるのはこの列挙のみ。独自フィールドを作らない)
159
+
160
+ 索引 (indexTemplate)
161
+ - 全体は \`{{#entries}}...{{/entries}}\` で繰り返す
162
+ - 各エントリ内:
163
+ - \`{{title}}\` / \`{{#mainImage}}<img src="{{.}}" alt="">{{/mainImage}}\`
164
+ - \`{{publishedAt}}\`(「YYYY年M月D日」形式。\`{{publishedDate}}\` は YYYY-MM-DD、\`{{publishedYear}}\` / \`{{publishedMonth}}\` / \`{{publishedDay}}\` も可)
165
+ - \`{{#tags}}<span class="tag">{{.}}</span>{{/tags}}\`(分類ラベル)
166
+ - \`{{detailUrl}}\` を \`<a href="{{detailUrl}}">\` で使う
167
+ - 必要なら \`{{themeColor}}\` を CSS 内で使ってよい
168
+
169
+ 詳細 (detailTemplate)
170
+ - \`<h1>{{title}}</h1>\`
171
+ - \`{{#publishedAt}}<time>{{.}}</time>{{/publishedAt}}\`
172
+ - \`{{#tags}}<span class="tag">{{.}}</span>{{/tags}}\`
173
+ - \`{{#mainImage}}<img src="{{.}}" alt="">{{/mainImage}}\`
174
+ - \`<div class="cms-body">{{{bodyHtml}}}</div>\`(三重波括弧で生 HTML)
175
+ - \`{{#subImages}}<img src="{{.}}" alt="">{{/subImages}}\`
176
+ - 本文内の見出し / リンク / 引用 / コード / テーブル等にスタイルを当てる
177
+
178
+ ## CSS の整合性ルール(必ず守る)
179
+
180
+ - セレクタは独自クラス(\`.cms-index\`, \`.cms-card\` 等)を使い、グローバル要素 (body, h1, * など) を直接書かない
181
+ - 索引のルート class は \`.cms-index\`、詳細のルート class は \`.cms-detail\` を必ず付ける(CSS スコープ統一)
182
+ - 書いた CSS で参照する class は必ず同じテンプレ内の HTML に出現させる(死にセレクタ禁止)
183
+ - \`grid-template-columns\` の列数と、その中で出力するセル数を一致させる
184
+ - \`@import\` / 外部フォントの url() は新規追加しない
185
+
186
+ ## Mustache 制約(混入しがちなバグを避ける)
187
+
188
+ - **同名 section の二重ネスト**(\`{{#tags}}...{{#tags}}...{{/tags}}...{{/tags}}\`)は禁止 — 全項目が重複表示される
189
+ - **非標準 helper は無し** — \`{{#@last}}\` / \`{{#@first}}\` / \`{{#@index}}\` などの Handlebars / lodash 由来 helper はサポートしていない。区切りが要るなら CSS の \`:not(:last-child)\` で表現
190
+ - **未定義変数は空文字** になる。AI が \`{{#match}}\` のような独自フィールドを発明しても render は通って何も出ない(バグの温床)
191
+ - 使えるのは下記 Mustache 変数のみ。独自フィールド禁止
192
+ - サーバ側で create 時に sample data で dry-run render される(構文エラーや上記の問題は 422 で弾かれる)。422 が返ってきたら出力を直して再 create
193
+
194
+ ## 装飾の二重付与に注意
195
+
196
+ - \`{{publishedAt}}\` は CLI 側で「YYYY年M月D日」に整形済み。テンプレで \`{{publishedAt}}年\` と書くと「2026年1月1日年」になる
197
+ - データ側(エントリの title 等)に「様」「@」のような装飾を含めない前提でテンプレを書く。**装飾はテンプレ側に集約**
198
+
199
+ ## replaceTargetSelector
200
+
201
+ 元ページに既に記事一覧があれば、その**ラッパー要素を一意に指す CSS セレクタ** を返す(例: \`section.news-list\`, \`#articles\`)。ID 優先 → class → 階層指定。ページ内で 1 つだけマッチする形に。なければ空文字。
202
+
203
+ ## detailRemoveSelectors
204
+
205
+ 詳細ページ(\`/<slug>/<entry-slug>.html\`)はこのページのシェルを再利用して表示されます。**この一覧ページ専用の装飾**(ページヒーロー、リスト導入文、リスト用ヘッダー画像など)は詳細ページに残るとおかしいので、シェルから除外する CSS セレクタを最大 5 個返してください。サイト共通ヘッダー/フッター/ロゴは含めない。
206
+
207
+ ## 出力 JSON スキーマ
208
+
209
+ \`\`\`json
210
+ {
211
+ "name": "string(コレクション名の提案。例: お知らせ / 実績 / ブログ)",
212
+ "themeColor": "#RRGGBB | \\"\\"",
213
+ "indexTemplate": "string(<style> + HTML)",
214
+ "detailTemplate": "string(<style> + HTML)",
215
+ "replaceTargetSelector": "string | \\"\\"",
216
+ "detailRemoveSelectors": ["string", ...],
217
+ "warnings": ["string", ...]
218
+ }
219
+ \`\`\`
220
+
221
+ 抽出不能なら理由を説明(JSON は出力しない)。
222
+
223
+ ## 完了したら
224
+
225
+ 1. 上記 JSON を一時ファイル(例: \`/tmp/42ws-collection.json\`)に書き出す
226
+ 2. 次のコマンドで 42ws に登録:
227
+
228
+ \`\`\`
229
+ 42ws collections create ${createCmdTail}
230
+ \`\`\`
231
+
232
+ ${inject ? '`--inject` 付きなので、`collections create` が元ソースの `replaceTargetSelector` 部分(または `<main>` / `</body>` のヒューリスティック)を embed スニペットに置き換えて再公開します。\n' : ''}
233
+ ---
234
+
235
+ ## HTML
236
+
237
+ \`\`\`html
238
+ ${html}
239
+ \`\`\`
240
+ `;
241
+ }
242
+ //# sourceMappingURL=import-prompts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"import-prompts.js","sourceRoot":"","sources":["../../src/lib/import-prompts.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@42ws/cli",
3
- "version": "0.0.49",
3
+ "version": "0.1.1",
4
4
  "description": "Publish static sites in one command",
5
5
  "publishConfig": {
6
6
  "access": "public"