@clawplays/ospec-cli 0.3.3 → 0.3.5

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 (35) hide show
  1. package/README.md +12 -1
  2. package/assets/for-ai/ar/ai-guide.md +55 -0
  3. package/assets/for-ai/ar/execution-protocol.md +44 -0
  4. package/assets/for-ai/ja-JP/ai-guide.md +55 -0
  5. package/assets/for-ai/ja-JP/execution-protocol.md +44 -0
  6. package/assets/project-conventions/ar/development-guide.md +31 -0
  7. package/assets/project-conventions/ar/naming-conventions.md +37 -0
  8. package/assets/project-conventions/ar/skill-conventions.md +33 -0
  9. package/assets/project-conventions/ar/workflow-conventions.md +40 -0
  10. package/assets/project-conventions/ja-JP/development-guide.md +31 -0
  11. package/assets/project-conventions/ja-JP/naming-conventions.md +45 -0
  12. package/assets/project-conventions/ja-JP/skill-conventions.md +33 -0
  13. package/assets/project-conventions/ja-JP/workflow-conventions.md +40 -0
  14. package/dist/cli.js +2 -2
  15. package/dist/commands/ArchiveCommand.js +36 -7
  16. package/dist/commands/NewCommand.js +39 -8
  17. package/dist/commands/UpdateCommand.js +109 -3
  18. package/dist/core/types.d.ts +5 -0
  19. package/dist/presets/ProjectPresets.d.ts +2 -2
  20. package/dist/presets/ProjectPresets.js +195 -69
  21. package/dist/services/ConfigManager.js +13 -0
  22. package/dist/services/ProjectAssetRegistry.d.ts +2 -2
  23. package/dist/services/ProjectAssetRegistry.js +12 -0
  24. package/dist/services/ProjectAssetService.js +7 -1
  25. package/dist/services/ProjectScaffoldCommandService.js +55 -21
  26. package/dist/services/ProjectScaffoldService.js +108 -12
  27. package/dist/services/ProjectService.js +504 -643
  28. package/dist/services/RunService.js +52 -6
  29. package/dist/services/templates/ExecutionTemplateBuilder.js +235 -9
  30. package/dist/services/templates/ProjectTemplateBuilder.js +878 -276
  31. package/dist/services/templates/TemplateBuilderBase.d.ts +2 -2
  32. package/dist/services/templates/TemplateBuilderBase.js +12 -3
  33. package/dist/services/templates/TemplateInputFactory.js +102 -47
  34. package/dist/services/templates/templateTypes.d.ts +1 -1
  35. package/package.json +1 -1
@@ -131,10 +131,10 @@ class NewCommand extends BaseCommand_1.BaseCommand {
131
131
  if (guideLanguage) {
132
132
  return guideLanguage;
133
133
  }
134
- return 'zh-CN';
134
+ return 'en-US';
135
135
  }
136
136
  normalizeDocumentLanguage(input) {
137
- return input === 'en-US' || input === 'zh-CN' ? input : null;
137
+ return input === 'en-US' || input === 'zh-CN' || input === 'ja-JP' || input === 'ar' ? input : null;
138
138
  }
139
139
  async readDocumentLanguageFromAssetManifest(targetDir) {
140
140
  const manifestPath = path.join(targetDir, '.ospec', 'asset-sources.json');
@@ -151,6 +151,12 @@ class NewCommand extends BaseCommand_1.BaseCommand {
151
151
  for (const targetRelativePath of ['for-ai/ai-guide.md', 'for-ai/execution-protocol.md']) {
152
152
  const asset = assets.find(item => item?.targetRelativePath === targetRelativePath);
153
153
  const sourceRelativePath = typeof asset?.sourceRelativePath === 'string' ? asset.sourceRelativePath : '';
154
+ if (sourceRelativePath.includes('/ar/')) {
155
+ return 'ar';
156
+ }
157
+ if (sourceRelativePath.includes('/ja-JP/')) {
158
+ return 'ja-JP';
159
+ }
154
160
  if (sourceRelativePath.includes('/en-US/')) {
155
161
  return 'en-US';
156
162
  }
@@ -171,18 +177,43 @@ class NewCommand extends BaseCommand_1.BaseCommand {
171
177
  }
172
178
  try {
173
179
  const content = await services_1.services.fileService.readFile(aiGuidePath);
174
- if (/[一-龥]/.test(content)) {
175
- return 'zh-CN';
176
- }
177
- if (/[A-Za-z]/.test(content)) {
178
- return 'en-US';
179
- }
180
+ return this.detectDocumentLanguageFromText(content) || null;
180
181
  }
181
182
  catch {
182
183
  return null;
183
184
  }
184
185
  return null;
185
186
  }
187
+ detectDocumentLanguageFromText(content) {
188
+ if (typeof content !== 'string' || content.trim().length === 0) {
189
+ return null;
190
+ }
191
+ if (/[\u0600-\u06FF]/.test(content)) {
192
+ return 'ar';
193
+ }
194
+ if (/[ぁ-ゟ゠-ヿ]/.test(content)) {
195
+ return 'ja-JP';
196
+ }
197
+ if (this.isLikelyJapaneseKanjiContent(content)) {
198
+ return 'ja-JP';
199
+ }
200
+ if (/[一-龥]/.test(content)) {
201
+ return 'zh-CN';
202
+ }
203
+ if (/[A-Za-z]/.test(content)) {
204
+ return 'en-US';
205
+ }
206
+ return null;
207
+ }
208
+ isLikelyJapaneseKanjiContent(content) {
209
+ if (!/[一-龥]/.test(content)) {
210
+ return false;
211
+ }
212
+ if (/[々〆ヵヶ「」『』]/.test(content)) {
213
+ return true;
214
+ }
215
+ return /(一覧|詳細|設定|権限|検索|構成|変更|確認|対応|連携|承認|申請|手順|履歴|機能|実装|設計|運用|画面|帳票|組織|拠点|区分|種別|完了|開始|終了|表示|取得|追加|削除|更新|登録)/.test(content);
216
+ }
186
217
  async ensureChangeNameAvailable(targetDir, featureName) {
187
218
  const activeDir = PathUtils_1.PathUtils.getChangeDir(targetDir, constants_1.DIR_NAMES.ACTIVE, featureName);
188
219
  const queuedDir = PathUtils_1.PathUtils.getChangeDir(targetDir, constants_1.DIR_NAMES.QUEUED, featureName);
@@ -18,8 +18,14 @@ class UpdateCommand extends BaseCommand_1.BaseCommand {
18
18
  const protocolResult = await services_1.services.projectService.syncProtocolGuidance(targetPath);
19
19
  const toolingResult = await this.syncProjectTooling(targetPath, protocolResult.documentLanguage);
20
20
  const pluginResult = await this.syncEnabledPluginAssets(targetPath);
21
+ const archiveResult = await this.syncArchiveLayout(targetPath);
21
22
  const skillResult = await this.syncInstalledSkills();
22
- const refreshedFiles = [...protocolResult.refreshedFiles, ...toolingResult.refreshedFiles, ...pluginResult.refreshedFiles];
23
+ const refreshedFiles = Array.from(new Set([
24
+ ...protocolResult.refreshedFiles,
25
+ ...toolingResult.refreshedFiles,
26
+ ...pluginResult.refreshedFiles,
27
+ ...(archiveResult.configSaved ? ['.skillrc'] : []),
28
+ ]));
23
29
  const createdFiles = [...protocolResult.createdFiles, ...toolingResult.createdFiles, ...pluginResult.createdFiles];
24
30
  const skippedFiles = [...protocolResult.skippedFiles, ...toolingResult.skippedFiles, ...pluginResult.skippedFiles];
25
31
  this.success(`Updated OSpec assets for ${protocolResult.projectName}`);
@@ -40,8 +46,14 @@ class UpdateCommand extends BaseCommand_1.BaseCommand {
40
46
  if (pluginResult.configSaved) {
41
47
  this.info(' plugin config normalized: .skillrc');
42
48
  }
43
- this.info(' note: update refreshes protocol docs, tooling, hooks, managed skills, and managed assets for already-enabled plugins');
44
- this.info(' note: it does not enable, disable, or migrate existing changes automatically');
49
+ if (archiveResult.configSaved) {
50
+ this.info(' archive layout normalized: .skillrc');
51
+ }
52
+ if (archiveResult.migratedChanges.length > 0) {
53
+ this.info(` archived changes migrated: ${archiveResult.migratedChanges.length}`);
54
+ }
55
+ this.info(' note: update refreshes protocol docs, tooling, hooks, managed skills, managed assets for already-enabled plugins, and the archive layout when needed');
56
+ this.info(' note: it does not enable, disable, or migrate active or queued changes automatically');
45
57
  }
46
58
  async syncProjectTooling(rootDir, documentLanguage) {
47
59
  const toolingPaths = [
@@ -222,6 +234,100 @@ class UpdateCommand extends BaseCommand_1.BaseCommand {
222
234
  }
223
235
  return { createdFiles, skippedFiles };
224
236
  }
237
+ async syncArchiveLayout(rootDir) {
238
+ const rawConfig = await services_1.services.fileService.readJSON((0, path_1.join)(rootDir, '.skillrc'));
239
+ const config = await services_1.services.configManager.loadConfig(rootDir);
240
+ const nextConfig = JSON.parse(JSON.stringify(config));
241
+ const archivedRoot = (0, path_1.join)(rootDir, 'changes', 'archived');
242
+ const migratedChanges = [];
243
+ if (await services_1.services.fileService.exists(archivedRoot)) {
244
+ const entryNames = (await services_1.services.fileService.readDir(archivedRoot)).sort((left, right) => left.localeCompare(right));
245
+ for (const entryName of entryNames) {
246
+ const entryPath = (0, path_1.join)(archivedRoot, entryName);
247
+ const stat = await services_1.services.fileService.stat(entryPath);
248
+ if (!stat.isDirectory()) {
249
+ continue;
250
+ }
251
+ const parsed = this.parseLegacyArchiveDirName(entryName);
252
+ if (!parsed) {
253
+ continue;
254
+ }
255
+ const archivedState = await this.readArchivedChangeState(entryPath);
256
+ if (!archivedState) {
257
+ continue;
258
+ }
259
+ const archiveDayRoot = (0, path_1.join)(archivedRoot, parsed.month, parsed.day);
260
+ await services_1.services.fileService.ensureDir(archiveDayRoot);
261
+ const targetPath = await this.resolveArchiveMigrationTarget(archiveDayRoot, archivedState.feature);
262
+ await services_1.services.fileService.move(entryPath, targetPath);
263
+ migratedChanges.push({
264
+ from: `changes/archived/${entryName}`,
265
+ to: this.toRelativePath(rootDir, targetPath),
266
+ });
267
+ }
268
+ }
269
+ let configSaved = false;
270
+ if (nextConfig.archive?.layout !== 'month-day') {
271
+ nextConfig.archive = {
272
+ ...(nextConfig.archive || {}),
273
+ layout: 'month-day',
274
+ };
275
+ await services_1.services.configManager.saveConfig(rootDir, nextConfig);
276
+ configSaved = true;
277
+ }
278
+ else if (!rawConfig?.archive || rawConfig.archive.layout !== 'month-day') {
279
+ await services_1.services.configManager.saveConfig(rootDir, nextConfig);
280
+ configSaved = true;
281
+ }
282
+ return {
283
+ configSaved,
284
+ migratedChanges,
285
+ };
286
+ }
287
+ parseLegacyArchiveDirName(entryName) {
288
+ const match = /^(\d{4}-\d{2}-\d{2})-(.+)$/.exec(entryName);
289
+ if (!match) {
290
+ return null;
291
+ }
292
+ return {
293
+ month: match[1].slice(0, 7),
294
+ day: match[1],
295
+ leafName: match[2],
296
+ };
297
+ }
298
+ async resolveArchiveMigrationTarget(archiveDayRoot, leafName) {
299
+ let candidate = leafName;
300
+ let index = 2;
301
+ while (await services_1.services.fileService.exists((0, path_1.join)(archiveDayRoot, candidate))) {
302
+ candidate = `${leafName}-${index}`;
303
+ index += 1;
304
+ }
305
+ return (0, path_1.join)(archiveDayRoot, candidate);
306
+ }
307
+ async readArchivedChangeState(entryPath) {
308
+ const statePath = (0, path_1.join)(entryPath, 'state.json');
309
+ if (!(await services_1.services.fileService.exists(statePath))) {
310
+ return null;
311
+ }
312
+ try {
313
+ const state = await services_1.services.fileService.readJSON(statePath);
314
+ if (typeof state?.feature !== 'string' || state.feature.trim().length === 0) {
315
+ return null;
316
+ }
317
+ if (state.status !== 'archived') {
318
+ return null;
319
+ }
320
+ return {
321
+ feature: state.feature.trim(),
322
+ };
323
+ }
324
+ catch {
325
+ return null;
326
+ }
327
+ }
328
+ toRelativePath(rootDir, targetPath) {
329
+ return (0, path_1.relative)(rootDir, targetPath).replace(/\\/g, '/');
330
+ }
225
331
  getManagedSkillNames() {
226
332
  return ['ospec', 'ospec-change'];
227
333
  }
@@ -89,9 +89,13 @@ export interface CheckpointPluginConfig {
89
89
  auto_pass_stitch_review: boolean;
90
90
  };
91
91
  }
92
+ export interface ArchiveConfig {
93
+ layout: 'flat' | 'month-day';
94
+ }
92
95
  export interface SkillrcConfig {
93
96
  version: string;
94
97
  mode: ProjectMode;
98
+ documentLanguage?: 'en-US' | 'zh-CN' | 'ja-JP' | 'ar';
95
99
  hooks: {
96
100
  'pre-commit': boolean;
97
101
  'post-merge': boolean;
@@ -103,6 +107,7 @@ export interface SkillrcConfig {
103
107
  include?: string[];
104
108
  exclude?: string[];
105
109
  };
110
+ archive?: ArchiveConfig;
106
111
  plugins?: {
107
112
  stitch?: StitchPluginConfig;
108
113
  checkpoint?: CheckpointPluginConfig;
@@ -1,6 +1,6 @@
1
1
  import { ProjectMode } from '../core/types';
2
2
  export type ProjectPresetId = 'official-site' | 'nextjs-web';
3
- export type ProjectPresetDocumentLanguage = 'zh-CN' | 'en-US';
3
+ export type ProjectPresetDocumentLanguage = 'zh-CN' | 'en-US' | 'ja-JP' | 'ar';
4
4
  export interface ProjectPresetFirstChangeSuggestion {
5
5
  name: string;
6
6
  background: string;
@@ -38,4 +38,4 @@ export declare const getLocalizedProjectPresetContent: (presetId: ProjectPresetI
38
38
  export declare const inferProjectPresetFromDescription: (description: string) => ProjectPresetDefinition | undefined;
39
39
  export declare const getProjectPresetFirstChangeSuggestion: (presetId: ProjectPresetId | null | undefined, language: ProjectPresetDocumentLanguage, projectName: string) => ProjectPresetFirstChangeSuggestion | null;
40
40
  export {};
41
- //# sourceMappingURL=ProjectPresets.d.ts.map
41
+ //# sourceMappingURL=ProjectPresets.d.ts.map
@@ -1,21 +1,10 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.getProjectPresetFirstChangeSuggestion = exports.inferProjectPresetFromDescription = exports.getLocalizedProjectPresetContent = exports.getProjectPresetById = exports.PROJECT_PRESETS = void 0;
4
- exports.PROJECT_PRESETS = [
5
- {
6
- id: 'official-site',
7
- name: 'Official Site',
8
- description: 'Marketing website with docs center, blog or changelog, admin content management, and auth.',
9
- recommendedMode: 'full',
10
- recommendedTechStack: ['Next.js', 'TypeScript', 'Tailwind CSS', 'Node.js'],
11
- architecture: 'Use a web/documentation/content architecture with a dedicated admin surface and shared auth boundaries.',
12
- modules: ['web', 'docs', 'content', 'admin', 'auth'],
13
- apiAreas: ['content api', 'auth api', 'admin api'],
14
- designDocs: ['ui information architecture', 'content model', 'cms workflow'],
15
- planningDocs: ['delivery plan', 'launch checklist'],
16
- keywords: ['官网', 'website', 'official site', 'docs', 'documentation', 'blog', 'changelog', 'cms'],
17
- buildFirstChangeSuggestion: (language, projectName) => language === 'zh-CN'
18
- ? {
4
+ const buildOfficialSiteSuggestion = (language, projectName) => {
5
+ switch (language) {
6
+ case 'zh-CN':
7
+ return {
19
8
  name: 'launch-official-site-foundation',
20
9
  background: `${projectName} 已完成初始框架搭建,下一步需要把官网首页、文档入口、内容流和后台入口串成第一版可演示链路。`,
21
10
  goals: [
@@ -32,8 +21,47 @@ exports.PROJECT_PRESETS = [
32
21
  ],
33
22
  affects: ['web', 'docs', 'content', 'admin', 'auth'],
34
23
  flags: ['multi_file_change'],
35
- }
36
- : {
24
+ };
25
+ case 'ja-JP':
26
+ return {
27
+ name: 'launch-official-site-foundation',
28
+ background: `${projectName} の初期骨格は整いました。次の change では、ホームページ、docs 導線、コンテンツフロー、admin 入口を最初の実演可能な縦スライスとして接続する必要があります。`,
29
+ goals: [
30
+ 'ホーム、docs、blog、admin、login をまたぐ主要ナビゲーションを接続する',
31
+ '公式サイト向けの最初の content / admin 境界を定義する',
32
+ '最初の change を公式サイト MVP の具体的な実装単位にする',
33
+ ],
34
+ inScope: ['web', 'docs', 'content', 'admin', 'auth'],
35
+ outOfScope: ['高度な RBAC', '本番向け CMS 連携', '完全な多言語コンテンツ基盤'],
36
+ acceptanceCriteria: [
37
+ 'ホームから docs、blog、admin、login へ遷移できる',
38
+ 'docs / blog / admin ページにプレースホルダー構造とモジュール意図がある',
39
+ 'proposal / tasks / verification が公式サイト MVP 範囲を明確にカバーする',
40
+ ],
41
+ affects: ['web', 'docs', 'content', 'admin', 'auth'],
42
+ flags: ['multi_file_change'],
43
+ };
44
+ case 'ar':
45
+ return {
46
+ name: 'launch-official-site-foundation',
47
+ background: `أصبح لدى ${projectName} هيكل أولي جاهز. يجب أن يربط التغيير التالي بين الصفحة الرئيسية ومدخل docs وتدفق المحتوى ومدخل الإدارة ليشكّل أول شريحة عمودية قابلة للعرض.`,
48
+ goals: [
49
+ 'ربط التنقل الرئيسي بين الصفحة الرئيسية وdocs والمدونة والإدارة وتسجيل الدخول',
50
+ 'تحديد أول حدود واضحة بين content وadmin للموقع الرسمي',
51
+ 'تحويل أول change إلى شريحة تنفيذية واضحة لـ MVP الموقع الرسمي',
52
+ ],
53
+ inScope: ['web', 'docs', 'content', 'admin', 'auth'],
54
+ outOfScope: ['نماذج صلاحيات معقدة', 'تكامل CMS إنتاجي', 'منظومة محتوى متعددة اللغات كاملة'],
55
+ acceptanceCriteria: [
56
+ 'يمكن التنقل من الصفحة الرئيسية إلى docs والمدونة والإدارة وتسجيل الدخول',
57
+ 'تحتوي صفحات docs والمدونة والإدارة على هياكل مؤقتة واضحة ونية وحدات مفهومة',
58
+ 'تغطي proposal وtasks وverification نطاق MVP الموقع الرسمي بوضوح',
59
+ ],
60
+ affects: ['web', 'docs', 'content', 'admin', 'auth'],
61
+ flags: ['multi_file_change'],
62
+ };
63
+ default:
64
+ return {
37
65
  name: 'launch-official-site-foundation',
38
66
  background: `${projectName} has the initial scaffold in place. The next change should connect the homepage, docs entry, content flow, and admin entry into the first demonstrable vertical slice.`,
39
67
  goals: [
@@ -50,22 +78,13 @@ exports.PROJECT_PRESETS = [
50
78
  ],
51
79
  affects: ['web', 'docs', 'content', 'admin', 'auth'],
52
80
  flags: ['multi_file_change'],
53
- },
54
- },
55
- {
56
- id: 'nextjs-web',
57
- name: 'Next.js Web App',
58
- description: 'General-purpose Next.js web product with web, account, and API surfaces.',
59
- recommendedMode: 'standard',
60
- recommendedTechStack: ['Next.js', 'TypeScript', 'Tailwind CSS', 'Node.js'],
61
- architecture: 'Use a web-first architecture with shared auth/account boundaries and clear API ownership.',
62
- modules: ['web', 'account', 'auth', 'api'],
63
- apiAreas: ['auth api', 'user api'],
64
- designDocs: ['ui information architecture', 'system architecture'],
65
- planningDocs: ['delivery plan'],
66
- keywords: ['next.js', 'nextjs', 'react', 'web app', 'web', 'portal'],
67
- buildFirstChangeSuggestion: (language, projectName) => language === 'zh-CN'
68
- ? {
81
+ };
82
+ }
83
+ };
84
+ const buildNextjsWebSuggestion = (language, projectName) => {
85
+ switch (language) {
86
+ case 'zh-CN':
87
+ return {
69
88
  name: 'establish-web-app-shell',
70
89
  background: `${projectName} 已初始化基础 Web App 骨架,下一步需要把首页、账户、登录与基础 API 入口连成第一版产品骨架。`,
71
90
  goals: [
@@ -82,8 +101,47 @@ exports.PROJECT_PRESETS = [
82
101
  ],
83
102
  affects: ['web', 'account', 'auth', 'api'],
84
103
  flags: ['multi_file_change'],
85
- }
86
- : {
104
+ };
105
+ case 'ja-JP':
106
+ return {
107
+ name: 'establish-web-app-shell',
108
+ background: `${projectName} には基本的な Web App の骨格があります。次の change では、ホーム、アカウント画面、ログイン導線、基本 API を接続して最初のプロダクトシェルを作る必要があります。`,
109
+ goals: [
110
+ 'ホーム、アカウント、ログインをまたぐ基本ナビゲーションを接続する',
111
+ 'auth api と user api の最初の責務境界を定義する',
112
+ '最初の change を継続的に拡張できるプロダクトシェルの節目にする',
113
+ ],
114
+ inScope: ['web', 'account', 'auth', 'api'],
115
+ outOfScope: ['決済フロー', '複雑な管理モジュール', '完全な通知システム'],
116
+ acceptanceCriteria: [
117
+ 'ホーム、アカウント、ログイン画面へ移動できる',
118
+ 'auth api と user api の文書が最初の実装スライスを支えられる',
119
+ '最初の change 文書が初期プロダクトシェルの範囲を明確に示す',
120
+ ],
121
+ affects: ['web', 'account', 'auth', 'api'],
122
+ flags: ['multi_file_change'],
123
+ };
124
+ case 'ar':
125
+ return {
126
+ name: 'establish-web-app-shell',
127
+ background: `أصبح لدى ${projectName} هيكل أساسي لتطبيق ويب. يجب أن يربط التغيير التالي بين الصفحة الرئيسية وسطح الحساب وتدفق تسجيل الدخول وواجهة API الأساسية لبناء أول غلاف منتج.`,
128
+ goals: [
129
+ 'ربط التنقل الأساسي بين الصفحة الرئيسية والحساب وتسجيل الدخول',
130
+ 'تحديد أول حدود مسؤولية بين auth api وuser api',
131
+ 'تحويل أول change إلى محطة واضحة لغلاف منتج قابل للتوسع',
132
+ ],
133
+ inScope: ['web', 'account', 'auth', 'api'],
134
+ outOfScope: ['المدفوعات', 'وحدات إدارة معقدة', 'أنظمة إشعارات كاملة'],
135
+ acceptanceCriteria: [
136
+ 'يمكن الوصول إلى الصفحة الرئيسية والحساب وتسجيل الدخول',
137
+ 'وثائق auth api وuser api كافية لشريحة التنفيذ الأولى',
138
+ 'تحدد وثائق أول change نطاق غلاف المنتج الأولي بوضوح',
139
+ ],
140
+ affects: ['web', 'account', 'auth', 'api'],
141
+ flags: ['multi_file_change'],
142
+ };
143
+ default:
144
+ return {
87
145
  name: 'establish-web-app-shell',
88
146
  background: `${projectName} now has the base web-app scaffold. The next change should connect the homepage, account surface, login flow, and base API surface into the first product shell.`,
89
147
  goals: [
@@ -100,7 +158,37 @@ exports.PROJECT_PRESETS = [
100
158
  ],
101
159
  affects: ['web', 'account', 'auth', 'api'],
102
160
  flags: ['multi_file_change'],
103
- },
161
+ };
162
+ }
163
+ };
164
+ exports.PROJECT_PRESETS = [
165
+ {
166
+ id: 'official-site',
167
+ name: 'Official Site',
168
+ description: 'Marketing website with docs center, blog or changelog, admin content management, and auth.',
169
+ recommendedMode: 'full',
170
+ recommendedTechStack: ['Next.js', 'TypeScript', 'Tailwind CSS', 'Node.js'],
171
+ architecture: 'Use a web/documentation/content architecture with a dedicated admin surface and shared auth boundaries.',
172
+ modules: ['web', 'docs', 'content', 'admin', 'auth'],
173
+ apiAreas: ['content api', 'auth api', 'admin api'],
174
+ designDocs: ['ui information architecture', 'content model', 'cms workflow'],
175
+ planningDocs: ['delivery plan', 'launch checklist'],
176
+ keywords: ['官网', 'website', 'official site', 'docs', 'documentation', 'blog', 'changelog', 'cms', '公式サイト', 'ドキュメント', 'ブログ', '変更履歴', 'موقع', 'توثيق', 'مدونة', 'سجل التغييرات'],
177
+ buildFirstChangeSuggestion: buildOfficialSiteSuggestion,
178
+ },
179
+ {
180
+ id: 'nextjs-web',
181
+ name: 'Next.js Web App',
182
+ description: 'General-purpose Next.js web product with web, account, and API surfaces.',
183
+ recommendedMode: 'standard',
184
+ recommendedTechStack: ['Next.js', 'TypeScript', 'Tailwind CSS', 'Node.js'],
185
+ architecture: 'Use a web-first architecture with shared auth/account boundaries and clear API ownership.',
186
+ modules: ['web', 'account', 'auth', 'api'],
187
+ apiAreas: ['auth api', 'user api'],
188
+ designDocs: ['ui information architecture', 'system architecture'],
189
+ planningDocs: ['delivery plan'],
190
+ keywords: ['next.js', 'nextjs', 'react', 'web app', 'web', 'portal', 'ウェブアプリ', 'ポータル', 'تطبيق ويب', 'بوابة'],
191
+ buildFirstChangeSuggestion: buildNextjsWebSuggestion,
104
192
  },
105
193
  ];
106
194
  const getProjectPresetById = (presetId) => exports.PROJECT_PRESETS.find(preset => preset.id === presetId);
@@ -110,38 +198,76 @@ const getLocalizedProjectPresetContent = (presetId, language) => {
110
198
  return null;
111
199
  }
112
200
  if (presetId === 'official-site') {
113
- return language === 'zh-CN'
114
- ? {
115
- name: '官网站点',
116
- description: '用于官网展示、文档中心、博客/更新日志、后台内容管理与鉴权的一体化官网项目。',
117
- architecture: '采用 web / docs / content 分层,并提供独立 admin 面与共享 auth 边界。',
118
- designDocs: ['界面信息架构', '内容模型', 'CMS 工作流'],
119
- planningDocs: ['交付计划', '上线检查清单'],
120
- }
121
- : {
122
- name: 'Official Site',
123
- description: 'Marketing website with docs center, blog or changelog, admin content management, and auth.',
124
- architecture: 'Use a web/documentation/content architecture with a dedicated admin surface and shared auth boundaries.',
125
- designDocs: ['ui information architecture', 'content model', 'cms workflow'],
126
- planningDocs: ['delivery plan', 'launch checklist'],
127
- };
201
+ switch (language) {
202
+ case 'zh-CN':
203
+ return {
204
+ name: '官网站点',
205
+ description: '用于官网展示、文档中心、博客/更新日志、后台内容管理与鉴权的一体化官网项目。',
206
+ architecture: '采用 web / docs / content 分层,并提供独立 admin 面与共享 auth 边界。',
207
+ designDocs: ['界面信息架构', '内容模型', 'CMS 工作流'],
208
+ planningDocs: ['交付计划', '上线检查清单'],
209
+ };
210
+ case 'ja-JP':
211
+ return {
212
+ name: '公式サイト',
213
+ description: '公式サイト、docs センター、ブログ / 変更履歴、admin コンテンツ管理、auth をまとめた一体型プロジェクト。',
214
+ architecture: 'web / docs / content の分離を基本にし、専用の admin 面と共有 auth 境界を持つ構成を採用します。',
215
+ designDocs: ['UI 情報設計', 'コンテンツモデル', 'CMS ワークフロー'],
216
+ planningDocs: ['配信計画', 'ローンチチェックリスト'],
217
+ };
218
+ case 'ar':
219
+ return {
220
+ name: 'الموقع الرسمي',
221
+ description: 'مشروع موحد للموقع الرسمي مع مركز docs ومدونة أو سجل تغييرات وإدارة محتوى عبر admin مع auth.',
222
+ architecture: 'اعتمد معمارية web / docs / content مع سطح admin مخصص وحدود auth مشتركة.',
223
+ designDocs: ['هيكلة معلومات الواجهة', 'نموذج المحتوى', 'سير عمل CMS'],
224
+ planningDocs: ['خطة التسليم', 'قائمة فحص الإطلاق'],
225
+ };
226
+ default:
227
+ return {
228
+ name: 'Official Site',
229
+ description: 'Marketing website with docs center, blog or changelog, admin content management, and auth.',
230
+ architecture: 'Use a web/documentation/content architecture with a dedicated admin surface and shared auth boundaries.',
231
+ designDocs: ['ui information architecture', 'content model', 'cms workflow'],
232
+ planningDocs: ['delivery plan', 'launch checklist'],
233
+ };
234
+ }
128
235
  }
129
236
  if (presetId === 'nextjs-web') {
130
- return language === 'zh-CN'
131
- ? {
132
- name: 'Next.js Web 应用',
133
- description: '适用于标准 Web 产品的 Next.js 项目骨架,内置 web、account、auth 与 API 边界。',
134
- architecture: '采用以 web 为中心的架构,并保持 account / auth / api 的清晰职责边界。',
135
- designDocs: ['界面信息架构', '系统架构'],
136
- planningDocs: ['交付计划'],
137
- }
138
- : {
139
- name: 'Next.js Web App',
140
- description: 'General-purpose Next.js web product with web, account, and API surfaces.',
141
- architecture: 'Use a web-first architecture with shared auth/account boundaries and clear API ownership.',
142
- designDocs: ['ui information architecture', 'system architecture'],
143
- planningDocs: ['delivery plan'],
144
- };
237
+ switch (language) {
238
+ case 'zh-CN':
239
+ return {
240
+ name: 'Next.js Web 应用',
241
+ description: '适用于标准 Web 产品的 Next.js 项目骨架,内置 web、account、auth API 边界。',
242
+ architecture: '采用以 web 为中心的架构,并保持 account / auth / api 的清晰职责边界。',
243
+ designDocs: ['界面信息架构', '系统架构'],
244
+ planningDocs: ['交付计划'],
245
+ };
246
+ case 'ja-JP':
247
+ return {
248
+ name: 'Next.js Web アプリ',
249
+ description: 'web、account、auth、API の境界を備えた汎用的な Next.js Web プロダクト用の初期構成です。',
250
+ architecture: 'web を中心にしつつ、auth / account の共有境界と明確な API 所有権を持つ構成を採用します。',
251
+ designDocs: ['UI 情報設計', 'システムアーキテクチャ'],
252
+ planningDocs: ['配信計画'],
253
+ };
254
+ case 'ar':
255
+ return {
256
+ name: 'تطبيق ويب Next.js',
257
+ description: 'هيكل مشروع Next.js عام لمنتج ويب مع أسطح web وaccount وAPI وحدود واضحة.',
258
+ architecture: 'اعتمد معمارية تتمحور حول web مع حدود auth/account مشتركة وملكية API واضحة.',
259
+ designDocs: ['هيكلة معلومات الواجهة', 'معمارية النظام'],
260
+ planningDocs: ['خطة التسليم'],
261
+ };
262
+ default:
263
+ return {
264
+ name: 'Next.js Web App',
265
+ description: 'General-purpose Next.js web product with web, account, and API surfaces.',
266
+ architecture: 'Use a web-first architecture with shared auth/account boundaries and clear API ownership.',
267
+ designDocs: ['ui information architecture', 'system architecture'],
268
+ planningDocs: ['delivery plan'],
269
+ };
270
+ }
145
271
  }
146
272
  return null;
147
273
  };
@@ -152,10 +278,10 @@ const inferProjectPresetFromDescription = (description) => {
152
278
  const matchedKeywords = preset.keywords.filter(keyword => normalized.includes(keyword.toLowerCase()));
153
279
  const weightedScore = matchedKeywords.reduce((score, keyword) => {
154
280
  const normalizedKeyword = keyword.toLowerCase();
155
- if (['官网', 'official site', 'website', 'docs', 'documentation', 'blog', 'changelog', 'cms'].includes(normalizedKeyword)) {
281
+ if (['官网', 'official site', 'website', 'docs', 'documentation', 'blog', 'changelog', 'cms', '公式サイト', 'ドキュメント', 'ブログ', '変更履歴', 'موقع', 'توثيق', 'مدونة', 'سجل التغييرات'].includes(normalizedKeyword)) {
156
282
  return score + 3;
157
283
  }
158
- if (['next.js', 'nextjs', 'react', 'web app', 'portal'].includes(normalizedKeyword)) {
284
+ if (['next.js', 'nextjs', 'react', 'web app', 'portal', 'ウェブアプリ', 'ポータル', 'تطبيق ويب', 'بوابة'].includes(normalizedKeyword)) {
159
285
  return score + 2;
160
286
  }
161
287
  return score + 1;
@@ -187,4 +313,4 @@ const getProjectPresetFirstChangeSuggestion = (presetId, language, projectName)
187
313
  return preset.buildFirstChangeSuggestion(language, projectName);
188
314
  };
189
315
  exports.getProjectPresetFirstChangeSuggestion = getProjectPresetFirstChangeSuggestion;
190
- //# sourceMappingURL=ProjectPresets.js.map
316
+ //# sourceMappingURL=ProjectPresets.js.map
@@ -81,6 +81,9 @@ class ConfigManager {
81
81
  index: {
82
82
  exclude: ['node_modules/**', 'dist/**', '*.test.*'],
83
83
  },
84
+ archive: {
85
+ layout: 'month-day',
86
+ },
84
87
  plugins: this.createDefaultPluginsConfig(),
85
88
  workflow,
86
89
  };
@@ -91,6 +94,11 @@ class ConfigManager {
91
94
  checkpoint: PluginWorkflowComposer_1.DEFAULT_CHECKPOINT_PLUGIN_CONFIG,
92
95
  }));
93
96
  }
97
+ normalizeDocumentLanguage(input) {
98
+ return input === 'en-US' || input === 'zh-CN' || input === 'ja-JP' || input === 'ar'
99
+ ? input
100
+ : undefined;
101
+ }
94
102
  normalizePluginsConfig(plugins) {
95
103
  const defaults = this.createDefaultPluginsConfig();
96
104
  const stitch = plugins?.stitch && typeof plugins.stitch === 'object' ? plugins.stitch : {};
@@ -340,6 +348,7 @@ class ConfigManager {
340
348
  }
341
349
  normalizeConfig(config) {
342
350
  const mode = ['lite', 'standard', 'full'].includes(config.mode) ? config.mode : 'full';
351
+ const archive = config.archive && typeof config.archive === 'object' ? config.archive : {};
343
352
  const hooks = config.hooks || {
344
353
  'pre-commit': true,
345
354
  'post-merge': true,
@@ -364,6 +373,10 @@ class ConfigManager {
364
373
  ...config,
365
374
  version: config.version === '3.0' ? '4.0' : config.version,
366
375
  mode,
376
+ documentLanguage: this.normalizeDocumentLanguage(config.documentLanguage),
377
+ archive: {
378
+ layout: archive.layout === 'month-day' ? 'month-day' : 'flat',
379
+ },
367
380
  hooks: {
368
381
  ...normalizedHooks,
369
382
  ...(legacyWarnDefaults
@@ -5,8 +5,8 @@ export interface DirectCopyProjectAssetDefinition {
5
5
  description: string;
6
6
  targetRelativePath: string;
7
7
  overwritePolicy: 'if_missing';
8
- localizedSources?: Partial<Record<'zh-CN' | 'en-US', string>>;
8
+ localizedSources?: Partial<Record<'zh-CN' | 'en-US' | 'ja-JP' | 'ar', string>>;
9
9
  sourceRelativePaths?: string[];
10
10
  }
11
11
  export declare const DIRECT_COPY_PROJECT_ASSETS: DirectCopyProjectAssetDefinition[];
12
- //# sourceMappingURL=ProjectAssetRegistry.d.ts.map
12
+ //# sourceMappingURL=ProjectAssetRegistry.d.ts.map