@clawplays/ospec-cli 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.
Files changed (191) hide show
  1. package/.ospec/templates/hooks/post-merge +8 -0
  2. package/.ospec/templates/hooks/pre-commit +8 -0
  3. package/LICENSE +21 -0
  4. package/README.md +549 -0
  5. package/README.zh-CN.md +549 -0
  6. package/assets/for-ai/en-US/ai-guide.md +98 -0
  7. package/assets/for-ai/en-US/execution-protocol.md +64 -0
  8. package/assets/for-ai/zh-CN/ai-guide.md +102 -0
  9. package/assets/for-ai/zh-CN/execution-protocol.md +68 -0
  10. package/assets/git-hooks/post-merge +12 -0
  11. package/assets/git-hooks/pre-commit +12 -0
  12. package/assets/global-skills/claude/ospec-change/SKILL.md +116 -0
  13. package/assets/global-skills/codex/ospec-change/SKILL.md +117 -0
  14. package/assets/global-skills/codex/ospec-change/agents/openai.yaml +7 -0
  15. package/assets/global-skills/codex/ospec-change/skill.yaml +19 -0
  16. package/assets/project-conventions/en-US/development-guide.md +32 -0
  17. package/assets/project-conventions/en-US/naming-conventions.md +51 -0
  18. package/assets/project-conventions/en-US/skill-conventions.md +40 -0
  19. package/assets/project-conventions/en-US/workflow-conventions.md +70 -0
  20. package/assets/project-conventions/zh-CN/development-guide.md +32 -0
  21. package/assets/project-conventions/zh-CN/naming-conventions.md +51 -0
  22. package/assets/project-conventions/zh-CN/skill-conventions.md +40 -0
  23. package/assets/project-conventions/zh-CN/workflow-conventions.md +74 -0
  24. package/dist/adapters/codex-stitch-adapter.js +420 -0
  25. package/dist/adapters/gemini-stitch-adapter.js +408 -0
  26. package/dist/adapters/playwright-checkpoint-adapter.js +2260 -0
  27. package/dist/advanced/BatchOperations.d.ts +36 -0
  28. package/dist/advanced/BatchOperations.js +159 -0
  29. package/dist/advanced/CachingLayer.d.ts +66 -0
  30. package/dist/advanced/CachingLayer.js +136 -0
  31. package/dist/advanced/FeatureUpdater.d.ts +46 -0
  32. package/dist/advanced/FeatureUpdater.js +151 -0
  33. package/dist/advanced/PerformanceMonitor.d.ts +52 -0
  34. package/dist/advanced/PerformanceMonitor.js +129 -0
  35. package/dist/advanced/StatePersistence.d.ts +61 -0
  36. package/dist/advanced/StatePersistence.js +168 -0
  37. package/dist/advanced/index.d.ts +14 -0
  38. package/dist/advanced/index.js +22 -0
  39. package/dist/cli/commands/config.d.ts +5 -0
  40. package/dist/cli/commands/config.js +6 -0
  41. package/dist/cli/commands/feature.d.ts +5 -0
  42. package/dist/cli/commands/feature.js +6 -0
  43. package/dist/cli/commands/index.d.ts +5 -0
  44. package/dist/cli/commands/index.js +6 -0
  45. package/dist/cli/commands/project.d.ts +5 -0
  46. package/dist/cli/commands/project.js +6 -0
  47. package/dist/cli/commands/validate.d.ts +5 -0
  48. package/dist/cli/commands/validate.js +6 -0
  49. package/dist/cli/index.d.ts +5 -0
  50. package/dist/cli/index.js +6 -0
  51. package/dist/cli.d.ts +3 -0
  52. package/dist/cli.js +1007 -0
  53. package/dist/commands/ArchiveCommand.d.ts +14 -0
  54. package/dist/commands/ArchiveCommand.js +241 -0
  55. package/dist/commands/BaseCommand.d.ts +33 -0
  56. package/dist/commands/BaseCommand.js +46 -0
  57. package/dist/commands/BatchCommand.d.ts +5 -0
  58. package/dist/commands/BatchCommand.js +42 -0
  59. package/dist/commands/ChangesCommand.d.ts +3 -0
  60. package/dist/commands/ChangesCommand.js +71 -0
  61. package/dist/commands/DocsCommand.d.ts +5 -0
  62. package/dist/commands/DocsCommand.js +118 -0
  63. package/dist/commands/FinalizeCommand.d.ts +3 -0
  64. package/dist/commands/FinalizeCommand.js +24 -0
  65. package/dist/commands/IndexCommand.d.ts +5 -0
  66. package/dist/commands/IndexCommand.js +57 -0
  67. package/dist/commands/InitCommand.d.ts +5 -0
  68. package/dist/commands/InitCommand.js +65 -0
  69. package/dist/commands/NewCommand.d.ts +11 -0
  70. package/dist/commands/NewCommand.js +262 -0
  71. package/dist/commands/PluginsCommand.d.ts +58 -0
  72. package/dist/commands/PluginsCommand.js +2491 -0
  73. package/dist/commands/ProgressCommand.d.ts +5 -0
  74. package/dist/commands/ProgressCommand.js +103 -0
  75. package/dist/commands/QueueCommand.d.ts +10 -0
  76. package/dist/commands/QueueCommand.js +147 -0
  77. package/dist/commands/RunCommand.d.ts +13 -0
  78. package/dist/commands/RunCommand.js +200 -0
  79. package/dist/commands/SkillCommand.d.ts +31 -0
  80. package/dist/commands/SkillCommand.js +1216 -0
  81. package/dist/commands/SkillsCommand.d.ts +5 -0
  82. package/dist/commands/SkillsCommand.js +68 -0
  83. package/dist/commands/StatusCommand.d.ts +6 -0
  84. package/dist/commands/StatusCommand.js +140 -0
  85. package/dist/commands/UpdateCommand.d.ts +8 -0
  86. package/dist/commands/UpdateCommand.js +251 -0
  87. package/dist/commands/VerifyCommand.d.ts +5 -0
  88. package/dist/commands/VerifyCommand.js +278 -0
  89. package/dist/commands/WorkflowCommand.d.ts +12 -0
  90. package/dist/commands/WorkflowCommand.js +150 -0
  91. package/dist/commands/index.d.ts +43 -0
  92. package/dist/commands/index.js +85 -0
  93. package/dist/core/constants.d.ts +41 -0
  94. package/dist/core/constants.js +73 -0
  95. package/dist/core/errors.d.ts +36 -0
  96. package/dist/core/errors.js +72 -0
  97. package/dist/core/index.d.ts +7 -0
  98. package/dist/core/index.js +23 -0
  99. package/dist/core/types.d.ts +369 -0
  100. package/dist/core/types.js +3 -0
  101. package/dist/index.d.ts +11 -0
  102. package/dist/index.js +27 -0
  103. package/dist/presets/ProjectPresets.d.ts +41 -0
  104. package/dist/presets/ProjectPresets.js +190 -0
  105. package/dist/scaffolds/ProjectScaffoldPresets.d.ts +20 -0
  106. package/dist/scaffolds/ProjectScaffoldPresets.js +151 -0
  107. package/dist/services/ConfigManager.d.ts +14 -0
  108. package/dist/services/ConfigManager.js +386 -0
  109. package/dist/services/FeatureManager.d.ts +5 -0
  110. package/dist/services/FeatureManager.js +6 -0
  111. package/dist/services/FileService.d.ts +21 -0
  112. package/dist/services/FileService.js +152 -0
  113. package/dist/services/IndexBuilder.d.ts +12 -0
  114. package/dist/services/IndexBuilder.js +130 -0
  115. package/dist/services/Logger.d.ts +20 -0
  116. package/dist/services/Logger.js +48 -0
  117. package/dist/services/ProjectAssetRegistry.d.ts +12 -0
  118. package/dist/services/ProjectAssetRegistry.js +96 -0
  119. package/dist/services/ProjectAssetService.d.ts +49 -0
  120. package/dist/services/ProjectAssetService.js +223 -0
  121. package/dist/services/ProjectScaffoldCommandService.d.ts +73 -0
  122. package/dist/services/ProjectScaffoldCommandService.js +159 -0
  123. package/dist/services/ProjectScaffoldService.d.ts +44 -0
  124. package/dist/services/ProjectScaffoldService.js +507 -0
  125. package/dist/services/ProjectService.d.ts +209 -0
  126. package/dist/services/ProjectService.js +13239 -0
  127. package/dist/services/QueueService.d.ts +17 -0
  128. package/dist/services/QueueService.js +142 -0
  129. package/dist/services/RunService.d.ts +40 -0
  130. package/dist/services/RunService.js +420 -0
  131. package/dist/services/SkillParser.d.ts +30 -0
  132. package/dist/services/SkillParser.js +88 -0
  133. package/dist/services/StateManager.d.ts +16 -0
  134. package/dist/services/StateManager.js +127 -0
  135. package/dist/services/TemplateEngine.d.ts +43 -0
  136. package/dist/services/TemplateEngine.js +119 -0
  137. package/dist/services/TemplateGenerator.d.ts +40 -0
  138. package/dist/services/TemplateGenerator.js +273 -0
  139. package/dist/services/ValidationService.d.ts +19 -0
  140. package/dist/services/ValidationService.js +44 -0
  141. package/dist/services/Validator.d.ts +5 -0
  142. package/dist/services/Validator.js +6 -0
  143. package/dist/services/index.d.ts +52 -0
  144. package/dist/services/index.js +91 -0
  145. package/dist/services/templates/ExecutionTemplateBuilder.d.ts +12 -0
  146. package/dist/services/templates/ExecutionTemplateBuilder.js +300 -0
  147. package/dist/services/templates/ProjectTemplateBuilder.d.ts +38 -0
  148. package/dist/services/templates/ProjectTemplateBuilder.js +1897 -0
  149. package/dist/services/templates/TemplateBuilderBase.d.ts +19 -0
  150. package/dist/services/templates/TemplateBuilderBase.js +60 -0
  151. package/dist/services/templates/TemplateInputFactory.d.ts +16 -0
  152. package/dist/services/templates/TemplateInputFactory.js +298 -0
  153. package/dist/services/templates/templateTypes.d.ts +90 -0
  154. package/dist/services/templates/templateTypes.js +3 -0
  155. package/dist/tools/build-index.js +632 -0
  156. package/dist/utils/DateUtils.d.ts +18 -0
  157. package/dist/utils/DateUtils.js +40 -0
  158. package/dist/utils/PathUtils.d.ts +9 -0
  159. package/dist/utils/PathUtils.js +66 -0
  160. package/dist/utils/StringUtils.d.ts +26 -0
  161. package/dist/utils/StringUtils.js +47 -0
  162. package/dist/utils/helpers.d.ts +5 -0
  163. package/dist/utils/helpers.js +6 -0
  164. package/dist/utils/index.d.ts +7 -0
  165. package/dist/utils/index.js +23 -0
  166. package/dist/utils/logger.d.ts +5 -0
  167. package/dist/utils/logger.js +6 -0
  168. package/dist/utils/path.d.ts +5 -0
  169. package/dist/utils/path.js +6 -0
  170. package/dist/utils/subcommandHelp.d.ts +11 -0
  171. package/dist/utils/subcommandHelp.js +119 -0
  172. package/dist/workflow/ArchiveGate.d.ts +30 -0
  173. package/dist/workflow/ArchiveGate.js +93 -0
  174. package/dist/workflow/ConfigurableWorkflow.d.ts +89 -0
  175. package/dist/workflow/ConfigurableWorkflow.js +186 -0
  176. package/dist/workflow/HookSystem.d.ts +38 -0
  177. package/dist/workflow/HookSystem.js +66 -0
  178. package/dist/workflow/IndexRegenerator.d.ts +49 -0
  179. package/dist/workflow/IndexRegenerator.js +147 -0
  180. package/dist/workflow/PluginWorkflowComposer.d.ts +138 -0
  181. package/dist/workflow/PluginWorkflowComposer.js +239 -0
  182. package/dist/workflow/SkillUpdateEngine.d.ts +26 -0
  183. package/dist/workflow/SkillUpdateEngine.js +113 -0
  184. package/dist/workflow/VerificationSystem.d.ts +24 -0
  185. package/dist/workflow/VerificationSystem.js +116 -0
  186. package/dist/workflow/WorkflowEngine.d.ts +15 -0
  187. package/dist/workflow/WorkflowEngine.js +57 -0
  188. package/dist/workflow/index.d.ts +19 -0
  189. package/dist/workflow/index.js +32 -0
  190. package/package.json +78 -0
  191. package/scripts/postinstall.js +43 -0
@@ -0,0 +1,507 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.createProjectScaffoldService = exports.ProjectScaffoldService = void 0;
7
+ const path_1 = __importDefault(require("path"));
8
+ const ProjectScaffoldPresets_1 = require("../scaffolds/ProjectScaffoldPresets");
9
+ class ProjectScaffoldService {
10
+ constructor(fileService) {
11
+ this.fileService = fileService;
12
+ }
13
+ getPlan(normalized) {
14
+ const preset = (0, ProjectScaffoldPresets_1.getProjectScaffoldPreset)(normalized.projectPresetId);
15
+ if (!preset) {
16
+ return null;
17
+ }
18
+ const localizedMeta = (0, ProjectScaffoldPresets_1.getLocalizedProjectScaffoldMeta)(preset.presetId, normalized.documentLanguage);
19
+ return {
20
+ presetId: preset.presetId,
21
+ title: localizedMeta.title,
22
+ description: localizedMeta.description,
23
+ framework: localizedMeta.framework,
24
+ installCommand: localizedMeta.installCommand,
25
+ directories: [...preset.directories],
26
+ files: preset.files.map(file => ({
27
+ ...file,
28
+ purpose: (0, ProjectScaffoldPresets_1.getLocalizedProjectScaffoldPurpose)(file.path, normalized.documentLanguage) || file.purpose,
29
+ })),
30
+ newDirectories: [...preset.directories],
31
+ existingDirectories: [],
32
+ newFiles: preset.files.map(file => file.path),
33
+ existingFiles: [],
34
+ };
35
+ }
36
+ async getPlanForProject(rootDir, normalized) {
37
+ const basePlan = this.getPlan(normalized);
38
+ if (!basePlan) {
39
+ return null;
40
+ }
41
+ const newDirectories = [];
42
+ const existingDirectories = [];
43
+ for (const directory of basePlan.directories) {
44
+ const targetPath = path_1.default.join(rootDir, ...directory.split('/'));
45
+ if (await this.fileService.exists(targetPath)) {
46
+ existingDirectories.push(directory);
47
+ }
48
+ else {
49
+ newDirectories.push(directory);
50
+ }
51
+ }
52
+ const newFiles = [];
53
+ const existingFiles = [];
54
+ for (const file of basePlan.files) {
55
+ const targetPath = path_1.default.join(rootDir, ...file.path.split('/'));
56
+ if (await this.fileService.exists(targetPath)) {
57
+ existingFiles.push(file.path);
58
+ }
59
+ else {
60
+ newFiles.push(file.path);
61
+ }
62
+ }
63
+ return {
64
+ ...basePlan,
65
+ newDirectories,
66
+ existingDirectories,
67
+ newFiles,
68
+ existingFiles,
69
+ };
70
+ }
71
+ getGeneratedPaths(normalized) {
72
+ const plan = this.getPlan(normalized);
73
+ if (!plan) {
74
+ return [];
75
+ }
76
+ return plan.files.map(file => file.path);
77
+ }
78
+ async applyScaffold(rootDir, normalized) {
79
+ const plan = await this.getPlanForProject(rootDir, normalized);
80
+ const preset = (0, ProjectScaffoldPresets_1.getProjectScaffoldPreset)(normalized.projectPresetId);
81
+ if (!plan || !preset) {
82
+ return null;
83
+ }
84
+ const createdDirectories = [];
85
+ const skippedDirectories = [];
86
+ for (const directory of preset.directories) {
87
+ await this.fileService.ensureDir(path_1.default.join(rootDir, ...directory.split('/')));
88
+ if (plan.existingDirectories.includes(directory)) {
89
+ skippedDirectories.push(directory);
90
+ }
91
+ else {
92
+ createdDirectories.push(directory);
93
+ }
94
+ }
95
+ const createdFiles = [];
96
+ const skippedFiles = [];
97
+ for (const file of preset.files) {
98
+ const targetPath = path_1.default.join(rootDir, ...file.path.split('/'));
99
+ if (await this.fileService.exists(targetPath)) {
100
+ skippedFiles.push(file.path);
101
+ continue;
102
+ }
103
+ await this.fileService.writeFile(targetPath, this.renderFileContent(file.path, preset, normalized));
104
+ createdFiles.push(file.path);
105
+ }
106
+ return {
107
+ plan,
108
+ createdDirectories,
109
+ skippedDirectories,
110
+ createdFiles,
111
+ skippedFiles,
112
+ };
113
+ }
114
+ renderFileContent(filePath, preset, normalized) {
115
+ switch (filePath) {
116
+ case '.gitignore':
117
+ return `node_modules
118
+ .next
119
+ out
120
+ dist
121
+ coverage
122
+ .env
123
+ .env.local
124
+ `;
125
+ case 'package.json':
126
+ return this.renderPackageJson(normalized);
127
+ case 'tsconfig.json':
128
+ return `{
129
+ "compilerOptions": {
130
+ "target": "ES2017",
131
+ "lib": ["dom", "dom.iterable", "esnext"],
132
+ "allowJs": false,
133
+ "skipLibCheck": true,
134
+ "strict": true,
135
+ "noEmit": true,
136
+ "esModuleInterop": true,
137
+ "module": "esnext",
138
+ "moduleResolution": "bundler",
139
+ "resolveJsonModule": true,
140
+ "isolatedModules": true,
141
+ "jsx": "preserve",
142
+ "incremental": true,
143
+ "plugins": [{ "name": "next" }]
144
+ },
145
+ "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
146
+ "exclude": ["node_modules"]
147
+ }
148
+ `;
149
+ case 'next-env.d.ts':
150
+ return `/// <reference types="next" />
151
+ /// <reference types="next/image-types/global" />
152
+
153
+ // This file is auto-generated by Next.js.
154
+ `;
155
+ case 'next.config.mjs':
156
+ return `/** @type {import('next').NextConfig} */
157
+ const nextConfig = {
158
+ reactStrictMode: true,
159
+ };
160
+
161
+ export default nextConfig;
162
+ `;
163
+ case 'postcss.config.js':
164
+ return `module.exports = {
165
+ plugins: {
166
+ tailwindcss: {},
167
+ autoprefixer: {},
168
+ },
169
+ };
170
+ `;
171
+ case 'tailwind.config.ts':
172
+ return `import type { Config } from 'tailwindcss';
173
+
174
+ const config: Config = {
175
+ content: [
176
+ './app/**/*.{js,ts,jsx,tsx,mdx}',
177
+ './src/**/*.{js,ts,jsx,tsx,mdx}',
178
+ ],
179
+ theme: {
180
+ extend: {},
181
+ },
182
+ plugins: [],
183
+ };
184
+
185
+ export default config;
186
+ `;
187
+ case 'app/globals.css':
188
+ return `@tailwind base;
189
+ @tailwind components;
190
+ @tailwind utilities;
191
+
192
+ :root {
193
+ color-scheme: light;
194
+ --background: #f5efe3;
195
+ --foreground: #111111;
196
+ --surface: #fffaf2;
197
+ --surface-strong: #f1e7d6;
198
+ --line: #d8ccb8;
199
+ --accent: #b65a2a;
200
+ --accent-strong: #8e3f18;
201
+ }
202
+
203
+ * {
204
+ box-sizing: border-box;
205
+ }
206
+
207
+ html,
208
+ body {
209
+ margin: 0;
210
+ padding: 0;
211
+ background: radial-gradient(circle at top, rgba(182, 90, 42, 0.18), transparent 32%), var(--background);
212
+ color: var(--foreground);
213
+ font-family: "Segoe UI", sans-serif;
214
+ }
215
+
216
+ a {
217
+ color: inherit;
218
+ text-decoration: none;
219
+ }
220
+ `;
221
+ case 'app/layout.tsx':
222
+ return `import type { Metadata } from 'next';
223
+ import './globals.css';
224
+ import { SiteShell } from '../src/components/site-shell';
225
+
226
+ export const metadata: Metadata = {
227
+ title: '${this.escapeTemplateString(normalized.projectName)}',
228
+ description: '${this.escapeTemplateString(normalized.summary)}',
229
+ };
230
+
231
+ export default function RootLayout({
232
+ children,
233
+ }: {
234
+ children: React.ReactNode;
235
+ }) {
236
+ return (
237
+ <html lang="${normalized.documentLanguage === 'zh-CN' ? 'zh-CN' : 'en'}">
238
+ <body>
239
+ <SiteShell>${'\n'} {children}${'\n'} </SiteShell>
240
+ </body>
241
+ </html>
242
+ );
243
+ }
244
+ `;
245
+ case 'app/page.tsx':
246
+ return this.renderHomePage(normalized);
247
+ case 'app/docs/page.tsx':
248
+ return this.renderSectionPage(normalized, normalized.documentLanguage === 'zh-CN' ? '文档中心' : 'Docs Center', normalized.documentLanguage === 'zh-CN'
249
+ ? '在这里沉淀架构说明、API 边界和团队执行流程。'
250
+ : 'Document architecture, API boundaries, and team workflows in one place.');
251
+ case 'app/blog/page.tsx':
252
+ return this.renderSectionPage(normalized, normalized.documentLanguage === 'zh-CN' ? '博客与更新日志' : 'Blog & Changelog', normalized.documentLanguage === 'zh-CN'
253
+ ? '在这里发布产品更新、版本发布说明和技术长文。'
254
+ : 'Publish product updates, release notes, and long-form technical posts.');
255
+ case 'app/admin/page.tsx':
256
+ return this.renderSectionPage(normalized, normalized.documentLanguage === 'zh-CN' ? '后台控制台' : 'Admin Console', normalized.documentLanguage === 'zh-CN'
257
+ ? '在这里承接内容工作流、发布条目和审核队列。'
258
+ : 'Operate content workflows, release entries, and editorial review queues.');
259
+ case 'app/login/page.tsx':
260
+ return this.renderSectionPage(normalized, normalized.documentLanguage === 'zh-CN' ? '鉴权入口' : 'Authentication', normalized.documentLanguage === 'zh-CN'
261
+ ? '后续可在这里接入身份提供方、角色策略和团队登录流程。'
262
+ : 'Connect your future identity provider, role policies, and staff sign-in flows here.');
263
+ case 'app/account/page.tsx':
264
+ return this.renderSectionPage(normalized, normalized.documentLanguage === 'zh-CN' ? '账户中心' : 'Account', normalized.documentLanguage === 'zh-CN'
265
+ ? '在这里管理登录用户的工作区、个人资料和团队级设置。'
266
+ : 'Manage the signed-in user workspace, profile, and team-level settings.');
267
+ case 'app/api/health/route.ts':
268
+ return `export async function GET() {
269
+ return Response.json({
270
+ ok: true,
271
+ project: '${this.escapeTemplateString(normalized.projectName)}',
272
+ preset: '${normalized.projectPresetId ?? ''}',
273
+ modules: ${JSON.stringify(normalized.modules)},
274
+ });
275
+ }
276
+ `;
277
+ case 'src/components/site-shell.tsx':
278
+ return this.renderSiteShell(normalized, preset);
279
+ case 'src/components/home-hero.tsx':
280
+ return this.renderHomeHero(normalized);
281
+ case 'src/lib/content/navigation.ts':
282
+ return this.renderNavigationFile(normalized, preset);
283
+ case 'src/lib/navigation/primary-nav.ts':
284
+ return this.renderNavigationFile(normalized, preset);
285
+ case 'src/lib/auth/session.ts':
286
+ return `export interface AppSession {
287
+ userId: string;
288
+ email: string;
289
+ roles: string[];
290
+ }
291
+
292
+ export async function getCurrentSession(): Promise<AppSession | null> {
293
+ return null;
294
+ }
295
+ `;
296
+ default:
297
+ return `# Placeholder for ${filePath}\n`;
298
+ }
299
+ }
300
+ renderPackageJson(normalized) {
301
+ return `${JSON.stringify({
302
+ name: this.toPackageName(normalized.projectName),
303
+ version: '0.1.0',
304
+ private: true,
305
+ scripts: {
306
+ dev: 'next dev',
307
+ build: 'next build',
308
+ start: 'next start',
309
+ lint: 'next lint',
310
+ },
311
+ dependencies: {
312
+ next: '^15.1.0',
313
+ react: '^19.0.0',
314
+ 'react-dom': '^19.0.0',
315
+ },
316
+ devDependencies: {
317
+ '@types/node': '^22.10.1',
318
+ '@types/react': '^19.0.0',
319
+ '@types/react-dom': '^19.0.0',
320
+ autoprefixer: '^10.4.20',
321
+ eslint: '^9.17.0',
322
+ 'eslint-config-next': '^15.1.0',
323
+ postcss: '^8.4.49',
324
+ tailwindcss: '^3.4.17',
325
+ typescript: '^5.7.2',
326
+ },
327
+ }, null, 2)}
328
+ `;
329
+ }
330
+ renderHomePage(normalized) {
331
+ const copy = this.getCopy(normalized);
332
+ const moduleCards = normalized.modules
333
+ .filter(moduleName => !this.isPlaceholder(moduleName))
334
+ .slice(0, 6)
335
+ .map(moduleName => ` <li className="rounded-2xl border border-stone-200 bg-white/80 px-4 py-3">
336
+ <div className="text-sm font-semibold text-stone-900">${this.escapeTemplateString(moduleName)}</div>
337
+ </li>`)
338
+ .join('\n');
339
+ return `import { HomeHero } from '../src/components/home-hero';
340
+
341
+ export default function HomePage() {
342
+ return (
343
+ <main className="mx-auto max-w-6xl px-6 py-16">
344
+ <HomeHero />
345
+ <section className="mt-10 grid gap-6 lg:grid-cols-[1.2fr_0.8fr]">
346
+ <article className="rounded-[2rem] border border-stone-200 bg-[var(--surface)] p-8 shadow-sm">
347
+ <p className="text-xs uppercase tracking-[0.2em] text-stone-500">${copy.projectSummary}</p>
348
+ <p className="mt-4 text-lg leading-8 text-stone-700">${this.escapeTemplateString(normalized.summary)}</p>
349
+ </article>
350
+ <article className="rounded-[2rem] border border-stone-200 bg-white/85 p-8 shadow-sm">
351
+ <p className="text-xs uppercase tracking-[0.2em] text-stone-500">${copy.initialModules}</p>
352
+ <ul className="mt-4 grid gap-3">
353
+ ${moduleCards || ` <li className="rounded-2xl border border-dashed border-stone-300 px-4 py-3 text-sm text-stone-500">${copy.fillModulesHint}</li>`}
354
+ </ul>
355
+ </article>
356
+ </section>
357
+ </main>
358
+ );
359
+ }
360
+ `;
361
+ }
362
+ renderSectionPage(normalized, title, description) {
363
+ const copy = this.getCopy(normalized);
364
+ return `export default function SectionPage() {
365
+ return (
366
+ <main className="mx-auto max-w-5xl px-6 py-16">
367
+ <section className="rounded-[2rem] border border-stone-200 bg-[var(--surface)] p-8 shadow-sm">
368
+ <p className="text-xs uppercase tracking-[0.2em] text-stone-500">${copy.projectLabel}</p>
369
+ <h1 className="mt-4 text-4xl font-semibold text-stone-950">${title}</h1>
370
+ <p className="mt-4 max-w-3xl text-base leading-8 text-stone-700">${description}</p>
371
+ </section>
372
+ </main>
373
+ );
374
+ }
375
+ `;
376
+ }
377
+ renderSiteShell(normalized, preset) {
378
+ const copy = this.getCopy(normalized);
379
+ const importPath = preset.presetId === 'nextjs-web' ? '../lib/navigation/primary-nav' : '../lib/content/navigation';
380
+ return `import Link from 'next/link';
381
+ import { navigationItems } from '${importPath}';
382
+
383
+ export function SiteShell({ children }: { children: React.ReactNode }) {
384
+ return (
385
+ <div className="min-h-screen">
386
+ <header className="border-b border-stone-200 bg-white/80 backdrop-blur">
387
+ <div className="mx-auto flex max-w-6xl items-center justify-between px-6 py-5">
388
+ <Link href="/" className="text-lg font-semibold text-stone-950">
389
+ ${this.escapeTemplateString(normalized.projectName)}
390
+ </Link>
391
+ <nav className="flex flex-wrap items-center gap-3 text-sm text-stone-600">
392
+ {navigationItems.map(item => (
393
+ <Link
394
+ key={item.href}
395
+ href={item.href}
396
+ className="rounded-full border border-stone-200 px-3 py-2 transition hover:border-[var(--accent)] hover:text-[var(--accent)]"
397
+ >
398
+ {item.label}
399
+ </Link>
400
+ ))}
401
+ </nav>
402
+ </div>
403
+ </header>
404
+ <div className="mx-auto max-w-6xl px-6 pt-5 text-xs uppercase tracking-[0.2em] text-stone-500">
405
+ ${copy.scaffoldBadge}
406
+ </div>
407
+ {children}
408
+ </div>
409
+ );
410
+ }
411
+ `;
412
+ }
413
+ renderHomeHero(normalized) {
414
+ const copy = this.getCopy(normalized);
415
+ return `export function HomeHero() {
416
+ return (
417
+ <section className="rounded-[2.5rem] border border-stone-200 bg-[linear-gradient(135deg,_rgba(182,90,42,0.18),_rgba(255,250,242,0.88))] px-8 py-14 shadow-sm">
418
+ <p className="text-xs uppercase tracking-[0.25em] text-stone-600">${copy.heroBadge}</p>
419
+ <h1 className="mt-5 max-w-4xl text-5xl font-semibold leading-tight text-stone-950">
420
+ ${this.escapeTemplateString(normalized.projectName)}
421
+ </h1>
422
+ <p className="mt-5 max-w-3xl text-lg leading-8 text-stone-700">
423
+ ${this.escapeTemplateString(normalized.summary)}
424
+ </p>
425
+ </section>
426
+ );
427
+ }
428
+ `;
429
+ }
430
+ renderNavigationFile(normalized, preset) {
431
+ const copy = this.getCopy(normalized);
432
+ const candidates = preset.presetId === 'nextjs-web'
433
+ ? [
434
+ { href: '/', label: copy.navHome, module: 'web' },
435
+ { href: '/account', label: copy.navAccount, module: 'account' },
436
+ { href: '/login', label: copy.navLogin, module: 'auth' },
437
+ ]
438
+ : [
439
+ { href: '/', label: copy.navHome, module: 'web' },
440
+ { href: '/docs', label: copy.navDocs, module: 'docs' },
441
+ { href: '/blog', label: copy.navBlog, module: 'content' },
442
+ { href: '/admin', label: copy.navAdmin, module: 'admin' },
443
+ { href: '/login', label: copy.navLogin, module: 'auth' },
444
+ ];
445
+ const activeModules = new Set(normalized.modules
446
+ .map(item => item.trim().toLowerCase())
447
+ .filter(Boolean));
448
+ const items = candidates.filter(item => activeModules.size === 0 || activeModules.has(item.module));
449
+ return `export const navigationItems = ${JSON.stringify(items.map(item => ({ href: item.href, label: item.label })), null, 2)} as const;
450
+ `;
451
+ }
452
+ toPackageName(projectName) {
453
+ const slug = projectName
454
+ .toLowerCase()
455
+ .replace(/[^a-z0-9]+/g, '-')
456
+ .replace(/^-+|-+$/g, '');
457
+ return slug || 'ospec-project';
458
+ }
459
+ isPlaceholder(value) {
460
+ const normalized = value.trim().toLowerCase();
461
+ return (!normalized ||
462
+ normalized === '待补充' ||
463
+ normalized === 'todo' ||
464
+ normalized === 'tbd' ||
465
+ normalized.includes('<') ||
466
+ normalized.includes('>'));
467
+ }
468
+ escapeTemplateString(value) {
469
+ return value.replace(/\\/g, '\\\\').replace(/'/g, "\\'");
470
+ }
471
+ getCopy(normalized) {
472
+ if (normalized.documentLanguage === 'zh-CN') {
473
+ return {
474
+ heroBadge: 'OSpec 脚手架',
475
+ scaffoldBadge: '已通过 OSpec 生成业务框架骨架',
476
+ projectSummary: '项目简介',
477
+ initialModules: '初始模块',
478
+ fillModulesHint: '后续请在 OSpec 项目文档中继续补全模块规划。',
479
+ projectLabel: '项目页面',
480
+ navHome: '首页',
481
+ navDocs: '文档',
482
+ navBlog: '博客',
483
+ navAdmin: '后台',
484
+ navLogin: '登录',
485
+ navAccount: '账户',
486
+ };
487
+ }
488
+ return {
489
+ heroBadge: 'OSpec Scaffold',
490
+ scaffoldBadge: 'Business scaffold generated by OSpec',
491
+ projectSummary: 'Project Summary',
492
+ initialModules: 'Initial Modules',
493
+ fillModulesHint: 'Continue refining module planning in OSpec project docs.',
494
+ projectLabel: 'Project Page',
495
+ navHome: 'Home',
496
+ navDocs: 'Docs',
497
+ navBlog: 'Blog',
498
+ navAdmin: 'Admin',
499
+ navLogin: 'Login',
500
+ navAccount: 'Account',
501
+ };
502
+ }
503
+ }
504
+ exports.ProjectScaffoldService = ProjectScaffoldService;
505
+ const createProjectScaffoldService = (fileService) => new ProjectScaffoldService(fileService);
506
+ exports.createProjectScaffoldService = createProjectScaffoldService;
507
+ //# sourceMappingURL=ProjectScaffoldService.js.map