create-ampless 0.2.0-alpha.9 → 1.0.0-alpha.39

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.
Files changed (84) hide show
  1. package/README.ja.md +77 -0
  2. package/README.md +4 -1
  3. package/dist/index.js +99 -47
  4. package/dist/templates/_shared/AGENTS.ja.md +104 -0
  5. package/dist/templates/_shared/AGENTS.md +104 -0
  6. package/dist/templates/_shared/README.ja.md +175 -0
  7. package/dist/templates/_shared/README.md +175 -0
  8. package/dist/templates/_shared/RUNBOOK.ja.md +120 -0
  9. package/dist/templates/_shared/RUNBOOK.md +24 -51
  10. package/dist/templates/_shared/amplify/auth/post-confirmation/resource.ts +6 -0
  11. package/dist/templates/_shared/amplify/backend.ts +2 -0
  12. package/dist/templates/_shared/amplify/data/get-published-post.js +9 -12
  13. package/dist/templates/_shared/amplify/data/list-posts-by-tag.js +6 -9
  14. package/dist/templates/_shared/amplify/data/list-published-posts.js +10 -11
  15. package/dist/templates/_shared/amplify/data/resource.custom.ts +1 -2
  16. package/dist/templates/_shared/amplify/data/resource.ts +22 -5
  17. package/dist/templates/_shared/amplify/events/processor-untrusted/resource.ts +4 -0
  18. package/dist/templates/_shared/amplify/functions/mcp-handler/handler.ts +1 -0
  19. package/dist/templates/_shared/amplify/functions/mcp-handler/resource.ts +11 -0
  20. package/dist/templates/_shared/amplify/functions/user-admin/resource.ts +6 -0
  21. package/dist/templates/_shared/app/(admin)/admin/mcp-tokens/page.tsx +5 -0
  22. package/dist/templates/_shared/app/globals.css +55 -39
  23. package/dist/templates/_shared/app/layout.tsx +24 -11
  24. package/dist/templates/_shared/app/site/[siteId]/r/[slug]/[[...path]]/route.ts +17 -0
  25. package/dist/templates/_shared/cms.config.ts +1 -23
  26. package/dist/templates/_shared/components/site-chrome/site-sidebar.tsx +2 -3
  27. package/dist/templates/_shared/lib/admin.ts +9 -7
  28. package/dist/templates/_shared/lib/posts-public.ts +1 -1
  29. package/dist/templates/_shared/package.json +11 -12
  30. package/dist/templates/_shared/proxy.ts +3 -3
  31. package/dist/templates/blog/README.ja.md +22 -0
  32. package/dist/templates/blog/README.md +17 -47
  33. package/dist/templates/blog/manifest.ts +1 -1
  34. package/dist/templates/blog/pages/feed.ts +3 -4
  35. package/dist/templates/blog/pages/home.tsx +5 -8
  36. package/dist/templates/blog/pages/post.tsx +7 -7
  37. package/dist/templates/blog/pages/sitemap.ts +3 -4
  38. package/dist/templates/blog/pages/tag.tsx +4 -4
  39. package/dist/templates/blog/tokens.css +26 -40
  40. package/dist/templates/corporate/README.ja.md +18 -0
  41. package/dist/templates/corporate/README.md +12 -14
  42. package/dist/templates/corporate/pages/feed.ts +2 -3
  43. package/dist/templates/corporate/pages/home.tsx +5 -8
  44. package/dist/templates/corporate/pages/post.tsx +7 -7
  45. package/dist/templates/corporate/pages/sitemap.ts +2 -3
  46. package/dist/templates/corporate/pages/tag.tsx +4 -4
  47. package/dist/templates/corporate/tokens.css +17 -39
  48. package/dist/templates/dads/README.ja.md +31 -0
  49. package/dist/templates/dads/README.md +13 -17
  50. package/dist/templates/dads/pages/feed.ts +2 -3
  51. package/dist/templates/dads/pages/home.tsx +5 -8
  52. package/dist/templates/dads/pages/post.tsx +7 -7
  53. package/dist/templates/dads/pages/sitemap.ts +2 -3
  54. package/dist/templates/dads/pages/tag.tsx +4 -4
  55. package/dist/templates/dads/tokens.css +22 -42
  56. package/dist/templates/docs/README.ja.md +24 -0
  57. package/dist/templates/docs/README.md +10 -13
  58. package/dist/templates/docs/pages/feed.ts +2 -3
  59. package/dist/templates/docs/pages/home.tsx +5 -6
  60. package/dist/templates/docs/pages/post.tsx +8 -8
  61. package/dist/templates/docs/pages/sitemap.ts +2 -3
  62. package/dist/templates/docs/pages/tag.tsx +5 -5
  63. package/dist/templates/docs/tokens.css +17 -39
  64. package/dist/templates/landing/README.ja.md +20 -0
  65. package/dist/templates/landing/README.md +14 -19
  66. package/dist/templates/landing/pages/feed.ts +3 -4
  67. package/dist/templates/landing/pages/home.tsx +5 -8
  68. package/dist/templates/landing/pages/post.tsx +7 -7
  69. package/dist/templates/landing/pages/sitemap.ts +2 -3
  70. package/dist/templates/landing/pages/tag.tsx +4 -4
  71. package/dist/templates/landing/tokens.css +17 -39
  72. package/dist/templates/minimal/README.ja.md +14 -0
  73. package/dist/templates/minimal/README.md +9 -47
  74. package/dist/templates/minimal/pages/feed.ts +3 -4
  75. package/dist/templates/minimal/pages/home.tsx +3 -4
  76. package/dist/templates/minimal/pages/post.tsx +6 -6
  77. package/dist/templates/minimal/pages/sitemap.ts +3 -4
  78. package/dist/templates/minimal/pages/tag.tsx +3 -3
  79. package/dist/templates/minimal/tokens.css +17 -39
  80. package/package.json +1 -1
  81. package/dist/templates/_shared/app/(admin)/admin/sites/page.tsx +0 -5
  82. package/dist/templates/_shared/app/site/[siteId]/raw/[slug]/route.ts +0 -5
  83. package/dist/templates/_shared/lib/admin-site-client.ts +0 -10
  84. package/dist/templates/_shared/lib/admin-site.ts +0 -12
package/README.ja.md ADDED
@@ -0,0 +1,77 @@
1
+ > English: [README.md](./README.md)
2
+ >
3
+
4
+ # create-ampless
5
+
6
+ [ampless](https://github.com/heavymoons/ampless) プロジェクト向け CLI スキャフォールディングツール。
7
+
8
+ > **プレリリース / アルファ版。** v1.0 まではマイナーバージョンでも破壊的変更が入る可能性があります。`@alpha` タグを使用してください(`@latest` タグは v1.0 まで存在しません)。
9
+
10
+ ```bash
11
+ npx create-ampless@alpha
12
+ ```
13
+
14
+ ウィザードが以下を順に案内します:
15
+
16
+ 1. プロジェクト名
17
+ 2. サイト名(デフォルトの `<title>` および OGP `siteName` として使用)
18
+ 3. テーマ — v0.1 では `blog`
19
+ 4. プラグイン — `seo`、`rss`、`webhook`
20
+
21
+ 出力は AWS Amplify Gen 2 バックエンド定義を含む Next.js 16(App Router)プロジェクトです。`/admin` に管理パネル、`/` にパブリックブログが設置され、選択したプラグインが `cms.config.ts` にあらかじめ設定されます。また運用メモ用の `RUNBOOK.md` も生成されます。
22
+
23
+ ## 生成されたプロジェクトでの次のステップ
24
+
25
+ ```bash
26
+ cd my-project
27
+ npm install
28
+ npx ampx sandbox # AWS 開発リソースをプロビジョニングし amplify_outputs.json を生成
29
+ npm run dev # http://localhost:3000
30
+ ```
31
+
32
+ `/login` でサインアップすると、最初に登録したユーザーが自動的に `ampless-admin` Cognito グループに昇格します。
33
+
34
+ ## ワンショットデプロイ:`--deploy`
35
+
36
+ ウィザードは `npx` から Amplify Hosting の URL まで一気に進めることもできます:
37
+
38
+ ```bash
39
+ npx create-ampless@alpha my-site --deploy
40
+ ```
41
+
42
+ このフラグを追加すると、スキャフォールディング後に以下が実行されます:
43
+
44
+ 1. `git init` + 初回コミット
45
+ 2. GitHub リポジトリを作成(`gh repo create`)してプッシュ
46
+ 3. 新しいリポジトリに紐付けた `aws amplify create-app`
47
+ 4. `aws amplify create-branch main`
48
+ 5. `aws amplify start-job --job-type RELEASE`
49
+ 6. `--domain` を指定した場合はオプションで `aws amplify create-domain-association`
50
+
51
+ コマンドラインに指定が不足している場合はインタラクティブに確認します。CI 向けのフル指定の例:
52
+
53
+ ```bash
54
+ npx create-ampless@alpha my-site --deploy \
55
+ --github-owner my-org \
56
+ --github-private \
57
+ --aws-region us-east-1 \
58
+ --domain example.com --subdomain blog \
59
+ --skip-confirm
60
+ ```
61
+
62
+ apex ドメインが同じ AWS アカウントの Route 53 でホストされている場合、ACM の検証が完了すると Amplify が DNS レコードを自動作成します。それ以外の場合、CLI がレジストラで追加すべき正確な CNAME を表示します。
63
+
64
+ ### デプロイの要件
65
+
66
+ - [`gh`](https://cli.github.com/) のインストールと認証(`gh auth login`)
67
+ - [`aws`](https://aws.amazon.com/cli/) のインストールと設定(`aws configure`)
68
+ - `repo` スコープを持つ GitHub トークン(`--github-token` → `GITHUB_TOKEN` 環境変数 → `gh auth token` → インタラクティブプロンプトの順で解決)
69
+
70
+ ## 動作要件
71
+
72
+ - Node.js >= 20
73
+ - AWS アカウントと `aws configure` の設定済み(サンドボックス / パイプラインデプロイは AWS に直接アクセスします)
74
+
75
+ ## ライセンス
76
+
77
+ [MIT](../../LICENSE)
package/README.md CHANGED
@@ -1,3 +1,6 @@
1
+ > 日本語版: [README.ja.md](./README.ja.md)
2
+ >
3
+
1
4
  # create-ampless
2
5
 
3
6
  CLI scaffolding tool for [ampless](https://github.com/heavymoons/ampless) projects.
@@ -15,7 +18,7 @@ The wizard walks you through:
15
18
  3. Theme — `blog` for v0.1
16
19
  4. Plugins — `seo`, `rss`, `webhook`
17
20
 
18
- Output is a Next.js 15 (App Router) project with the AWS Amplify Gen 2 backend definitions, an admin panel at `/admin`, public blog at `/`, the chosen plugins pre-wired in `cms.config.ts`, and a `RUNBOOK.md` for operations notes.
21
+ Output is a Next.js 16 (App Router) project with the AWS Amplify Gen 2 backend definitions, an admin panel at `/admin`, public blog at `/`, the chosen plugins pre-wired in `cms.config.ts`, and a `RUNBOOK.md` for operations notes.
19
22
 
20
23
  ## Next steps inside the generated project
21
24
 
package/dist/index.js CHANGED
@@ -435,9 +435,9 @@ function parseDeployArgs(argv) {
435
435
  var HELP_TEXT = `create-ampless \u2014 scaffold an ampless project
436
436
 
437
437
  Usage:
438
- npx create-ampless@alpha <project-name> [options]
439
- npx create-ampless@alpha --mount [options] # in an existing project dir
440
- npx create-ampless@alpha upgrade [options] # in an existing project dir
438
+ npx create-ampless@latest <project-name> [options]
439
+ npx create-ampless@latest --mount [options] # in an existing project dir
440
+ npx create-ampless@latest upgrade [options] # in an existing project dir
441
441
 
442
442
  Options:
443
443
  --site-name <name> Site display name (default: "My Blog")
@@ -487,7 +487,7 @@ copy-theme <source> <target>
487
487
  (the convention that flags it as user-owned, so upgrade leaves
488
488
  it alone). Run inside an existing ampless project.
489
489
 
490
- Example: npx create-ampless@alpha copy-theme blog my-blog
490
+ Example: npx create-ampless@latest copy-theme blog my-blog
491
491
  `;
492
492
 
493
493
  // src/deploy-prompts.ts
@@ -1091,38 +1091,19 @@ function splitDomain(domain, subdomain) {
1091
1091
  async function rewriteCmsConfigForDomain(projectDir, fullDomain) {
1092
1092
  const path = resolve4(projectDir, "cms.config.ts");
1093
1093
  if (!existsSync4(path)) {
1094
- return { urlRewritten: false, sitesInjected: false };
1094
+ return { urlRewritten: false };
1095
1095
  }
1096
1096
  let content = await readFile2(path, "utf-8");
1097
1097
  let urlRewritten = false;
1098
- let sitesInjected = false;
1099
1098
  const urlRe = /url:\s*['"]http:\/\/localhost:3000['"]/;
1100
1099
  if (urlRe.test(content)) {
1101
1100
  content = content.replace(urlRe, `url: 'https://${fullDomain}'`);
1102
1101
  urlRewritten = true;
1103
1102
  }
1104
- const sitesActiveRe = /^\s*sites:\s*\{/m;
1105
- if (!sitesActiveRe.test(content)) {
1106
- const siteCloseRe = /(\n(\s*)site:\s*\{[\s\S]*?\n\2\},)/;
1107
- const m = siteCloseRe.exec(content);
1108
- if (m) {
1109
- const indent = m[2] ?? " ";
1110
- const inner = indent + " ";
1111
- const innermost = inner + " ";
1112
- const inject = `
1113
- ${indent}sites: {
1114
- ${inner}default: {
1115
- ${innermost}domains: ['${fullDomain}'],
1116
- ${inner}},
1117
- ${indent}},`;
1118
- content = content.replace(siteCloseRe, `$1${inject}`);
1119
- sitesInjected = true;
1120
- }
1121
- }
1122
- if (urlRewritten || sitesInjected) {
1103
+ if (urlRewritten) {
1123
1104
  await writeFile2(path, content, "utf-8");
1124
1105
  }
1125
- return { urlRewritten, sitesInjected };
1106
+ return { urlRewritten };
1126
1107
  }
1127
1108
  async function run(cmd, args, opts = {}) {
1128
1109
  const { step, ...execaOpts } = opts;
@@ -1503,11 +1484,8 @@ async function runDeploy(opts) {
1503
1484
  const { registrable, subdomain } = splitDomain(opts.domain, opts.subdomain);
1504
1485
  const fullDomain = subdomain ? `${subdomain}.${registrable}` : registrable;
1505
1486
  const mutations = await rewriteCmsConfigForDomain(opts.projectDir, fullDomain);
1506
- if (mutations.urlRewritten || mutations.sitesInjected) {
1507
- const parts = [];
1508
- if (mutations.urlRewritten) parts.push(`site.url \u2192 https://${fullDomain}`);
1509
- if (mutations.sitesInjected) parts.push(`sites.default.domains += ${fullDomain}`);
1510
- log.info(`cms.config.ts updated: ${parts.join(", ")}`);
1487
+ if (mutations.urlRewritten) {
1488
+ log.info(`cms.config.ts updated: site.url \u2192 https://${fullDomain}`);
1511
1489
  }
1512
1490
  }
1513
1491
  const created = {};
@@ -1761,6 +1739,14 @@ import { join as join3, relative, extname as extname2, dirname } from "path";
1761
1739
  import { log as log3, outro } from "@clack/prompts";
1762
1740
  import pc3 from "picocolors";
1763
1741
  import { execa as execa4 } from "execa";
1742
+ var AMPLESS_MANAGED_APP_PATHS = [
1743
+ "app/(admin)/admin",
1744
+ "app/api/admin",
1745
+ "app/api/media",
1746
+ "app/api/mcp",
1747
+ "app/login",
1748
+ "app/site"
1749
+ ];
1764
1750
  var AMPLESS_PACKAGES = /* @__PURE__ */ new Set([
1765
1751
  "ampless",
1766
1752
  "@ampless/admin",
@@ -1882,6 +1868,63 @@ async function walkDir(dir, base, out) {
1882
1868
  }
1883
1869
  }
1884
1870
  }
1871
+ async function listFilesRecursive(root) {
1872
+ const out = [];
1873
+ async function walk(current, relPrefix) {
1874
+ const entries = await readdir3(current, { withFileTypes: true });
1875
+ for (const entry of entries) {
1876
+ const rel = relPrefix ? `${relPrefix}/${entry.name}` : entry.name;
1877
+ if (entry.isDirectory()) {
1878
+ await walk(join3(current, entry.name), rel);
1879
+ } else if (entry.isFile()) {
1880
+ out.push(rel);
1881
+ }
1882
+ }
1883
+ }
1884
+ if (!existsSync5(root)) return out;
1885
+ await walk(root, "");
1886
+ return out;
1887
+ }
1888
+ async function pruneEmptyDirs(root) {
1889
+ if (!existsSync5(root)) return;
1890
+ const entries = await readdir3(root, { withFileTypes: true });
1891
+ for (const entry of entries) {
1892
+ if (entry.isDirectory()) {
1893
+ await pruneEmptyDirs(join3(root, entry.name));
1894
+ }
1895
+ }
1896
+ const remaining = await readdir3(root);
1897
+ if (remaining.length === 0) {
1898
+ await rm2(root, { recursive: true, force: true });
1899
+ }
1900
+ }
1901
+ async function findObsoleteAppFiles(destDir, sharedDir) {
1902
+ const obsolete = [];
1903
+ for (const managedPath of AMPLESS_MANAGED_APP_PATHS) {
1904
+ const userPath = join3(destDir, managedPath);
1905
+ if (!existsSync5(userPath)) continue;
1906
+ const templatePath2 = join3(sharedDir, managedPath);
1907
+ const templateFiles = new Set(await listFilesRecursive(templatePath2));
1908
+ const userFiles = await listFilesRecursive(userPath);
1909
+ for (const f of userFiles) {
1910
+ if (!templateFiles.has(f)) {
1911
+ obsolete.push(`${managedPath}/${f}`);
1912
+ }
1913
+ }
1914
+ }
1915
+ return obsolete;
1916
+ }
1917
+ async function removeObsoleteAppFiles(destDir, paths) {
1918
+ for (const rel of paths) {
1919
+ const abs = join3(destDir, rel);
1920
+ if (existsSync5(abs)) {
1921
+ await rm2(abs, { force: true });
1922
+ }
1923
+ }
1924
+ for (const managedPath of AMPLESS_MANAGED_APP_PATHS) {
1925
+ await pruneEmptyDirs(join3(destDir, managedPath));
1926
+ }
1927
+ }
1885
1928
  function substituteVars(content, vars) {
1886
1929
  return content.replace(/\{\{(\w+)\}\}/g, (_, key) => vars[key] ?? `{{${key}}}`);
1887
1930
  }
@@ -1935,21 +1978,25 @@ async function runUpgradeIn(destDir, sharedDir, opts = {}) {
1935
1978
  const shippedThemes = themeSyncEnabled ? await listShippedThemes(templatesRoot) : [];
1936
1979
  const existingThemes = themeSyncEnabled ? await discoverInstalledThemes(destDir) : [];
1937
1980
  const preservedThemes = existingThemes.filter((t) => !shippedThemes.includes(t));
1981
+ const obsoleteFiles = await findObsoleteAppFiles(destDir, sharedDir);
1938
1982
  log3.info(
1939
- `replace: ${pc3.green(`\u8FFD\u52A0 ${replaceNew.length}`)} / ${pc3.yellow(`\u66F4\u65B0 ${replaceUpdate.length}`)}`
1983
+ `replace: ${pc3.green(`${replaceNew.length} added`)} / ${pc3.yellow(`${replaceUpdate.length} updated`)}`
1940
1984
  );
1941
- log3.info(`merge: ${pc3.cyan("package.json: ampless deps / managed scripts \u3092\u30C6\u30F3\u30D7\u30EC\u5074\u306B\u5408\u308F\u305B\u308B")}`);
1985
+ log3.info(`merge: ${pc3.cyan("package.json: sync ampless deps and managed scripts with the template")}`);
1942
1986
  if (classification.seed.length > 0) {
1943
1987
  log3.info(
1944
- `seed: ${pc3.green(`\u8FFD\u52A0 ${seedNew.length}`)} / ${pc3.dim(`\u65E2\u5B58\u30D5\u30A1\u30A4\u30EB\u306F\u4E0A\u66F8\u304D\u3057\u306A\u3044: ${seedSkipped.length}`)} (*.custom.ts)`
1988
+ `seed: ${pc3.green(`${seedNew.length} added`)} / ${pc3.dim(`${seedSkipped.length} kept (existing *.custom.ts left untouched)`)}`
1945
1989
  );
1946
1990
  }
1947
1991
  if (themeSyncEnabled) {
1948
1992
  log3.info(
1949
- `themes: ${pc3.cyan(`\u30C7\u30D5\u30A9\u30EB\u30C8\u30C6\u30FC\u30DE\u3092\u540C\u671F: ${shippedThemes.length}`)} / ${pc3.dim(`\u30AB\u30B9\u30BF\u30E0\u30C6\u30FC\u30DE (my-*) \u3092\u4FDD\u6301: ${preservedThemes.length}`)}`
1993
+ `themes: ${pc3.cyan(`${shippedThemes.length} default themes synced`)} / ${pc3.dim(`${preservedThemes.length} custom (my-*) themes preserved`)}`
1950
1994
  );
1951
1995
  }
1952
- log3.info(`protected: ${pc3.dim(`\u30C6\u30F3\u30D7\u30EC\u306B\u5B58\u5728\u3059\u308B\u304C\u89E6\u3089\u306A\u3044: ${classification.protected.length} \u500B`)}`);
1996
+ if (obsoleteFiles.length > 0) {
1997
+ log3.info(`cleanup: ${pc3.yellow(`${obsoleteFiles.length} removed`)} (files under ampless-managed app/ paths that no longer exist in the template)`);
1998
+ }
1999
+ log3.info(`protected: ${pc3.dim(`${classification.protected.length} template files left untouched`)}`);
1953
2000
  if (opts.dryRun) {
1954
2001
  return {
1955
2002
  added: replaceNew,
@@ -1958,7 +2005,8 @@ async function runUpgradeIn(destDir, sharedDir, opts = {}) {
1958
2005
  protected: classification.protected,
1959
2006
  themesSynced: shippedThemes,
1960
2007
  themesPreserved: preservedThemes,
1961
- packageJsonMerged: false
2008
+ packageJsonMerged: false,
2009
+ obsoleteRemoved: obsoleteFiles
1962
2010
  };
1963
2011
  }
1964
2012
  for (const rel of classification.replace) {
@@ -1972,6 +2020,7 @@ async function runUpgradeIn(destDir, sharedDir, opts = {}) {
1972
2020
  await copyWithSubstitution(src, dst, vars);
1973
2021
  }
1974
2022
  const themeResult = themeSyncEnabled ? await syncThemes(destDir, templatesRoot) : { synced: [], preserved: [] };
2023
+ await removeObsoleteAppFiles(destDir, obsoleteFiles);
1975
2024
  const templatePkgRaw = await readFile3(join3(sharedDir, "package.json"), "utf-8");
1976
2025
  const templatePkg = JSON.parse(templatePkgRaw);
1977
2026
  const indent = detectIndent(projectPkgRaw);
@@ -1988,9 +2037,12 @@ async function runUpgradeIn(destDir, sharedDir, opts = {}) {
1988
2037
  }
1989
2038
  const templateScripts = templatePkg["scripts"] ?? {};
1990
2039
  const projectScripts = projectPkg["scripts"] ?? {};
1991
- for (const [name, body] of Object.entries(templateScripts)) {
1992
- if (!AMPLESS_MANAGED_SCRIPTS.has(name)) continue;
1993
- projectScripts[name] = body;
2040
+ for (const name of AMPLESS_MANAGED_SCRIPTS) {
2041
+ if (name in templateScripts) {
2042
+ projectScripts[name] = templateScripts[name];
2043
+ } else {
2044
+ delete projectScripts[name];
2045
+ }
1994
2046
  }
1995
2047
  if (Object.keys(projectScripts).length > 0) {
1996
2048
  projectPkg["scripts"] = projectScripts;
@@ -2008,7 +2060,8 @@ async function runUpgradeIn(destDir, sharedDir, opts = {}) {
2008
2060
  protected: classification.protected,
2009
2061
  themesSynced: themeResult.synced,
2010
2062
  themesPreserved: themeResult.preserved,
2011
- packageJsonMerged: true
2063
+ packageJsonMerged: true,
2064
+ obsoleteRemoved: obsoleteFiles
2012
2065
  };
2013
2066
  }
2014
2067
  async function runUpgrade(args) {
@@ -2104,8 +2157,8 @@ async function runCopyTheme(args) {
2104
2157
  const source = args.copyThemeSource;
2105
2158
  const target = args.copyThemeTarget;
2106
2159
  if (!source || !target) {
2107
- log4.error("Usage: npx create-ampless@alpha copy-theme <source> <target>");
2108
- log4.info("Example: npx create-ampless@alpha copy-theme blog my-blog");
2160
+ log4.error("Usage: npx create-ampless@latest copy-theme <source> <target>");
2161
+ log4.info("Example: npx create-ampless@latest copy-theme blog my-blog");
2109
2162
  process.exit(1);
2110
2163
  }
2111
2164
  try {
@@ -2118,7 +2171,7 @@ async function runCopyTheme(args) {
2118
2171
 
2119
2172
  themes-registry.ts updated. Next:
2120
2173
  ${pc4.cyan(`Open themes/${result.target}/ and start customising`)}
2121
- ${pc4.cyan(`Activate via /admin/sites/<siteId>/theme`)}`
2174
+ ${pc4.cyan(`Activate via /admin/sites/default/theme`)}`
2122
2175
  );
2123
2176
  } catch (err) {
2124
2177
  log4.error(err instanceof Error ? err.message : String(err));
@@ -2163,7 +2216,7 @@ async function runMount(args) {
2163
2216
  if (problem) {
2164
2217
  log5.error(problem);
2165
2218
  log5.info(
2166
- "Run `npx create-ampless@alpha <name>` first to scaffold, or `cd` into a scaffolded project before passing --mount."
2219
+ "Run `npx create-ampless@latest <name>` first to scaffold, or `cd` into a scaffolded project before passing --mount."
2167
2220
  );
2168
2221
  process.exit(1);
2169
2222
  }
@@ -2269,8 +2322,7 @@ async function main() {
2269
2322
  Next steps:
2270
2323
  ${pc5.cyan("cd")} ${opts.projectName}
2271
2324
  ${pc5.cyan("npm install")}
2272
- ${pc5.cyan("npx ampx sandbox")} ${pc5.dim("# start Amplify backend")}
2273
- ${pc5.cyan("npm run dev")} ${pc5.dim("# start Next.js")}`
2325
+ ${pc5.cyan("npm run sandbox")} ${pc5.dim("# deploy sandbox + start Next.js")}`
2274
2326
  );
2275
2327
  }
2276
2328
  main().catch((err) => {
@@ -0,0 +1,104 @@
1
+ > English: [AGENTS.md](./AGENTS.md)
2
+ >
3
+ # AGENTS.md
4
+
5
+ このファイルは AI コーディングエージェント(Claude Code, Cursor, Codex など)が読む前提で書かれています。人間向けの日常利用ガイドは `README.ja.md` と `RUNBOOK.ja.md` を参照してください。
6
+
7
+ ## プロジェクトのレイアウト
8
+
9
+ - `themes/<name>/` — インストール済みのテーマ(tokens、pages、manifest)。アクティブテーマの切り替えは `/admin/sites/<id>/theme` で行う。
10
+ - `themes/my-<name>/` — ユーザーが所有するカスタマイズ済みのテーマコピー(`npm run copy-theme` で作成)。`my-` プレフィックスがついているコピーは `update-ampless` で上書きされない。
11
+ - `themes-registry.ts` — 自動生成ファイル。手動で編集しない。
12
+ - `amplify/` — TypeScript で定義された Amplify Gen 2 バックエンド(Cognito / DynamoDB / S3 / AppSync / Lambda)。
13
+ - `amplify_outputs.json` — `npm run sandbox` / Amplify Hosting が生成するファイル。編集しない。
14
+ - `app/` — Next.js 15 App Router(公開サイト + `/admin` UI)。
15
+ - `components/` — 共有 UI コンポーネント。
16
+ - `lib/` — 共有ユーティリティ(データアクセス、認証など)。
17
+ - `cms.config.ts` — サイト、プラグイン、デフォルト設定。
18
+ - `proxy.ts` — リクエストプロキシの設定。
19
+
20
+ ## 触っていい場所・ダメな場所
21
+
22
+ **自由に編集可能:**
23
+ - `themes/my-*/` — ユーザー自身のテーマコピー。
24
+ - `cms.config.ts` — サイト / プラグインの設定。
25
+ - 投稿コンテンツは管理 UI(推奨)または MCP サーバー経由で操作する。
26
+
27
+ **注意して触る(編集前に理由を説明すること):**
28
+ - `app/`、`components/`、`lib/` — 共有シェルの一部。`update-ampless` 後も編集は残るが、上流が同じファイルを変更した場合のマージはユーザー側の責任になる。テーマ関連の変更であれば、カスタムテーマ経由での拡張を優先する。
29
+ - `themes/<official-name>/`(`my-` プレフィックスなし)— 公式テーマは `update-ampless` で上書きされる。カスタマイズしたい場合は先に `npm run copy-theme <official-name> my-<your-name>` を実行し、コピーを編集する。
30
+ - `package.json` — `ampless` / `@ampless/*` のバージョンは一貫して管理する。バージョンアップには `update-ampless` を使う。
31
+
32
+ **編集禁止:**
33
+ - `themes-registry.ts` — scaffold / copy-theme / update コマンドが再生成する。
34
+ - `amplify/` — バックエンドのスキーマ変更はテーブルの再構築とサンドボックスデータの消失を招く可能性がある。触る前にユーザーの明示的な確認を得ること。
35
+ - `amplify_outputs.json` — `npm run sandbox` のたびに再生成される。
36
+ - `.amplify/` — Amplify CLI の作業ディレクトリ。
37
+ - `pnpm-lock.yaml` / `package-lock.json` — パッケージマネージャーに任せる。
38
+
39
+ ## テーマのカスタマイズ(標準的な手順)
40
+
41
+ 1. 公式テーマをコピーする:
42
+ ```bash
43
+ npm run copy-theme blog my-blog
44
+ ```
45
+ `my-` プレフィックスがついているコピーは `npm run update-ampless` で上書きされない。ターゲット名が `my-` で始まらない場合、コマンドは実行を拒否する。
46
+
47
+ 2. 以下の優先順位で編集する:
48
+ - `themes/my-blog/tokens.css` — 色、タイポグラフィ、スペーシング(`[data-theme='my-blog']` スコープの CSS カスタムプロパティ)。
49
+ - `themes/my-blog/manifest.ts` — 管理 UI(`/admin/sites/<id>/theme`)に公開するフィールドの宣言。
50
+ - `themes/my-blog/pages/` — レイアウト / 構造(Next.js ページモジュール: フィード、サイトマップ、投稿テンプレート)。
51
+
52
+ 3. テーマを有効化する: `/admin/sites/<siteId>/theme` を開き、`my-blog` を選択する。テーマ切り替えはランタイム設定 — 再デプロイは不要。
53
+
54
+ 4. 動作確認:
55
+ ```bash
56
+ npm run dev # http://localhost:3000
57
+ ```
58
+
59
+ ## MCP サーバー(`@ampless/mcp-server`)
60
+
61
+ エージェントが投稿コンテンツを直接クエリ・編集できる。公開ツール: `list_posts`、`get_post`、`create_post`、`update_post`、`delete_post`、`upload_media`、`get_schema`。
62
+
63
+ 登録方法:
64
+
65
+ プロジェクトルートの `.mcp.json` に追加する:
66
+ ```json
67
+ {
68
+ "mcpServers": {
69
+ "ampless": {
70
+ "command": "npx",
71
+ "args": ["-y", "@ampless/mcp-server", "--outputs", "./amplify_outputs.json"],
72
+ "env": {
73
+ "AMPLESS_MCP_EMAIL": "<your-admin-email>",
74
+ "AMPLESS_MCP_PASSWORD": "<your-admin-password>"
75
+ }
76
+ }
77
+ }
78
+ }
79
+ ```
80
+
81
+ 投稿本文は `markdown`、`html`、`tiptap`(JSON ドキュメント)の 3 フォーマットに対応している。
82
+
83
+ ## 動作確認の基準
84
+
85
+ 変更後は最低限以下を実行すること:
86
+
87
+ - `npm run dev` でブラウザから該当ページを開く。UI / テーマの変更は特に目視確認が必要(Playwright MCP が使える場合はスクリーンショットを活用する)。
88
+ - `npm run build` — 本番ビルドが成功することを確認する。
89
+ - `npm run lint`。
90
+
91
+ UI / テーマを変更した場合、型チェックだけを根拠にタスク完了を報告しない — ブラウザで実際に確認すること。
92
+
93
+ ## 既知の制約
94
+
95
+ - **サンドボックスのデータは揮発性。** スキーマに影響する変更は API とテーブルの再構築を引き起こす可能性がある。サンドボックスのコンテンツは使い捨てとして扱うこと。本番データは永続する。
96
+ - **1 Amplify デプロイ = 1 サイト。** 複数サイトを別ドメインで配信したい場合は、サイトごとに Amplify 環境を分けてデプロイする。スキーマの `siteId` カラムは常に `"default"` 固定(前方互換のため残してある)。
97
+ - **AppSync 公開 API キーは `amplify_outputs.json` に含まれ、サイト訪問者から見える。** 低信頼の認証情報として扱うこと — このキーの権限は公開済み投稿の読み取りのみ。`api-key-renewer` Lambda が毎月自動ローテーションするため、手動ローテーションは不要。
98
+ - **最初に登録したユーザーが管理者になる。** 以降のロール変更は Cognito コンソールで行う(RUNBOOK 参照)。
99
+
100
+ ## 参照先
101
+
102
+ - `README.ja.md` — サイト運営者向けの日常利用ガイド。
103
+ - `RUNBOOK.ja.md` — 定期的な運用手順(キーローテーション、バックアップ復元など)。
104
+ - `themes/<name>/README.ja.md` — テーマごとのカスタマイズ詳細。
@@ -0,0 +1,104 @@
1
+ > 日本語版: [AGENTS.ja.md](./AGENTS.ja.md)
2
+ >
3
+ # AGENTS.md
4
+
5
+ This file is written for AI coding agents (Claude Code, Cursor, Codex, etc.). For human-readable day-to-day guidance, see `README.md` and `RUNBOOK.md`.
6
+
7
+ ## Project layout
8
+
9
+ - `themes/<name>/` — installed themes (tokens, pages, manifest). Switch active theme at `/admin/sites/<id>/theme`.
10
+ - `themes/my-<name>/` — user-owned customised theme copies (created via `npm run copy-theme`). The `my-` prefix is what marks the copy as user-owned; `update-ampless` leaves anything under `themes/my-*/` alone.
11
+ - `themes-registry.ts` — auto-generated, do not hand-edit.
12
+ - `amplify/` — Amplify Gen 2 backend (Cognito / DynamoDB / S3 / AppSync / Lambda) defined in TypeScript.
13
+ - `amplify_outputs.json` — generated by `npm run sandbox` / Amplify Hosting. Do not edit.
14
+ - `app/` — Next.js 15 App Router (public site + `/admin` UI).
15
+ - `components/` — shared UI components.
16
+ - `lib/` — shared utilities (data access, auth, etc.).
17
+ - `cms.config.ts` — site, plugins, defaults.
18
+ - `proxy.ts` — request proxy config.
19
+
20
+ ## What you can and can't touch
21
+
22
+ **Free to edit:**
23
+ - `themes/my-*/` — your own theme copies.
24
+ - `cms.config.ts` — site/plugin config.
25
+ - Post content via the admin UI (recommended) or via the MCP server.
26
+
27
+ **Touch with caution (explain why before editing):**
28
+ - `app/`, `components/`, `lib/` — these are part of the shared shell. Edits survive `update-ampless` but you own the merge if upstream changes the same file. Prefer extending via a custom theme if the change is theme-related.
29
+ - `themes/<official-name>/` (no `my-` prefix) — official themes get overwritten by `update-ampless`. If you want to customise, run `npm run copy-theme <official-name> my-<your-name>` first and edit the copy.
30
+ - `package.json` — keep `ampless` / `@ampless/*` versions consistent. Use `update-ampless` to bump them.
31
+
32
+ **Do not edit:**
33
+ - `themes-registry.ts` — regenerated by the scaffold/copy-theme/update commands.
34
+ - `amplify/` — backend schema changes can rebuild tables and wipe sandbox data. Get explicit confirmation from the user before touching.
35
+ - `amplify_outputs.json` — regenerated each `npm run sandbox`.
36
+ - `.amplify/` — Amplify CLI working directory.
37
+ - `pnpm-lock.yaml` / `package-lock.json` — let the package manager update these.
38
+
39
+ ## Customising a theme (the standard flow)
40
+
41
+ 1. Copy an official theme:
42
+ ```bash
43
+ npm run copy-theme blog my-blog
44
+ ```
45
+ The `my-` prefix marks the copy as user-owned so `npm run update-ampless` leaves it alone. The target name must start with `my-` or the command refuses to run.
46
+
47
+ 2. Edit in this priority order:
48
+ - `themes/my-blog/tokens.css` — colours, typography, spacing (CSS custom properties scoped under `[data-theme='my-blog']`).
49
+ - `themes/my-blog/manifest.ts` — declare which fields are exposed in the admin UI (`/admin/sites/<id>/theme`).
50
+ - `themes/my-blog/pages/` — layout / structure (Next.js page modules: feed, sitemap, post templates).
51
+
52
+ 3. Activate the theme: open `/admin/sites/<siteId>/theme` and select `my-blog`. Theme switching is a runtime setting — no redeploy needed.
53
+
54
+ 4. Verify:
55
+ ```bash
56
+ npm run dev # http://localhost:3000
57
+ ```
58
+
59
+ ## MCP server (`@ampless/mcp-server`)
60
+
61
+ Lets agents query and modify post content directly. Tools exposed: `list_posts`, `get_post`, `create_post`, `update_post`, `delete_post`, `upload_media`, `get_schema`.
62
+
63
+ Registration:
64
+
65
+ Add to `.mcp.json` at the project root:
66
+ ```json
67
+ {
68
+ "mcpServers": {
69
+ "ampless": {
70
+ "command": "npx",
71
+ "args": ["-y", "@ampless/mcp-server", "--outputs", "./amplify_outputs.json"],
72
+ "env": {
73
+ "AMPLESS_MCP_EMAIL": "<your-admin-email>",
74
+ "AMPLESS_MCP_PASSWORD": "<your-admin-password>"
75
+ }
76
+ }
77
+ }
78
+ }
79
+ ```
80
+
81
+ Post bodies accept three formats: `markdown`, `html`, or `tiptap` (JSON document).
82
+
83
+ ## Verification expectations
84
+
85
+ After any change, run at minimum:
86
+
87
+ - `npm run dev` and load the affected page in a browser. UI changes especially require visual confirmation (use Playwright MCP for screenshots when available).
88
+ - `npm run build` — confirms the production build succeeds.
89
+ - `npm run lint`.
90
+
91
+ Do not report a task complete based solely on type checks if a UI/theme was modified — load it in a browser.
92
+
93
+ ## Known constraints
94
+
95
+ - **Sandbox data is ephemeral.** Schema-affecting changes can rebuild the API and tables; treat sandbox content as throw-away. Production data is durable.
96
+ - **One Amplify deployment = one site.** To serve multiple sites on different domains, deploy separate Amplify environments. The schema's `siteId` column is always the literal `"default"` (retained for forward-compat).
97
+ - **The AppSync public API key is shipped in `amplify_outputs.json` and visible to any site visitor.** Treat it as a low-trust credential — its only privilege is reading published posts. Auto-rotated monthly by the `api-key-renewer` Lambda; no manual rotation needed.
98
+ - **The first registered user becomes admin.** Subsequent role changes go through the Cognito console (see RUNBOOK).
99
+
100
+ ## See also
101
+
102
+ - `README.md` — day-to-day usage for site owners.
103
+ - `RUNBOOK.md` — occasional ops procedures (key rotation, backup restore, etc.).
104
+ - `themes/<name>/README.md` — per-theme customization details.