@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,130 @@
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.createIndexBuilder = exports.IndexBuilder = void 0;
7
+ const fs_extra_1 = __importDefault(require("fs-extra"));
8
+ const path_1 = __importDefault(require("path"));
9
+ const constants_1 = require("../core/constants");
10
+ const SKIP_DIRS = new Set(['node_modules', 'dist', '.git', 'changes', 'for-ai']);
11
+ class IndexBuilder {
12
+ constructor(skillParser) {
13
+ this.skillParser = skillParser;
14
+ }
15
+ async build(rootDir) {
16
+ const modules = {};
17
+ const tagIndex = {};
18
+ let totalFiles = 0;
19
+ let totalSections = 0;
20
+ const visit = async (currentDir) => {
21
+ const entries = (await fs_extra_1.default.readdir(currentDir, { withFileTypes: true })).sort((left, right) => left.name.localeCompare(right.name));
22
+ for (const entry of entries) {
23
+ const fullPath = path_1.default.join(currentDir, entry.name);
24
+ if (entry.isDirectory()) {
25
+ if (!SKIP_DIRS.has(entry.name)) {
26
+ await visit(fullPath);
27
+ }
28
+ continue;
29
+ }
30
+ if (entry.name !== constants_1.FILE_NAMES.SKILL_MD) {
31
+ continue;
32
+ }
33
+ totalFiles++;
34
+ const relativePath = path_1.default.relative(rootDir, fullPath).replace(/\\/g, '/');
35
+ const content = await fs_extra_1.default.readFile(fullPath, 'utf-8');
36
+ const parsed = this.skillParser.parseSkillFile(content);
37
+ const moduleName = parsed.frontmatter.name || relativePath;
38
+ const title = parsed.frontmatter.title || parsed.frontmatter.name || relativePath;
39
+ const tags = parsed.frontmatter.tags || [];
40
+ const sections = parsed.sections;
41
+ totalSections += Object.keys(sections).length;
42
+ modules[moduleName] = {
43
+ file: relativePath,
44
+ title,
45
+ tags,
46
+ sections,
47
+ };
48
+ for (const tag of tags) {
49
+ if (!tagIndex[tag]) {
50
+ tagIndex[tag] = [];
51
+ }
52
+ tagIndex[tag].push(moduleName);
53
+ }
54
+ }
55
+ };
56
+ if (await fs_extra_1.default.pathExists(rootDir)) {
57
+ await visit(rootDir);
58
+ }
59
+ const activeChangesDir = path_1.default.join(rootDir, 'changes', 'active');
60
+ const activeChanges = (await fs_extra_1.default.pathExists(activeChangesDir))
61
+ ? (await fs_extra_1.default.readdir(activeChangesDir)).sort((left, right) => left.localeCompare(right))
62
+ : [];
63
+ for (const tag of Object.keys(tagIndex)) {
64
+ tagIndex[tag] = tagIndex[tag].sort((left, right) => left.localeCompare(right));
65
+ }
66
+ return {
67
+ version: '1.0',
68
+ generated: new Date().toISOString(),
69
+ git_commit: null,
70
+ active_changes: activeChanges,
71
+ stats: {
72
+ totalFiles,
73
+ totalModules: Object.keys(modules).length,
74
+ totalSections,
75
+ },
76
+ modules,
77
+ tagIndex,
78
+ };
79
+ }
80
+ async write(rootDir) {
81
+ const indexPath = path_1.default.join(rootDir, constants_1.FILE_NAMES.SKILL_INDEX);
82
+ const previous = (await fs_extra_1.default.pathExists(indexPath))
83
+ ? (await fs_extra_1.default.readJson(indexPath))
84
+ : null;
85
+ const index = await this.build(rootDir);
86
+ const previousComparable = previous ? this.stripVolatileFields(previous) : null;
87
+ const nextComparable = this.stripVolatileFields(index);
88
+ if (previous && JSON.stringify(previousComparable) === JSON.stringify(nextComparable)) {
89
+ return previous;
90
+ }
91
+ const output = {
92
+ ...index,
93
+ generated: new Date().toISOString(),
94
+ };
95
+ await fs_extra_1.default.writeFile(indexPath, JSON.stringify(output, null, 2), 'utf-8');
96
+ return output;
97
+ }
98
+ async createEmpty(rootDir) {
99
+ const indexPath = path_1.default.join(rootDir, constants_1.FILE_NAMES.SKILL_INDEX);
100
+ const previous = (await fs_extra_1.default.pathExists(indexPath))
101
+ ? (await fs_extra_1.default.readJson(indexPath))
102
+ : null;
103
+ const index = {
104
+ version: '1.0',
105
+ generated: new Date().toISOString(),
106
+ git_commit: null,
107
+ active_changes: [],
108
+ stats: {
109
+ totalFiles: 0,
110
+ totalModules: 0,
111
+ totalSections: 0,
112
+ },
113
+ modules: {},
114
+ tagIndex: {},
115
+ };
116
+ if (previous && JSON.stringify(this.stripVolatileFields(previous)) === JSON.stringify(this.stripVolatileFields(index))) {
117
+ return previous;
118
+ }
119
+ await fs_extra_1.default.writeFile(indexPath, JSON.stringify(index, null, 2), 'utf-8');
120
+ return index;
121
+ }
122
+ stripVolatileFields(index) {
123
+ const { generated: _generated, ...stable } = index;
124
+ return stable;
125
+ }
126
+ }
127
+ exports.IndexBuilder = IndexBuilder;
128
+ const createIndexBuilder = (skillParser) => new IndexBuilder(skillParser);
129
+ exports.createIndexBuilder = createIndexBuilder;
130
+ //# sourceMappingURL=IndexBuilder.js.map
@@ -0,0 +1,20 @@
1
+ /**
2
+ * 日志服务
3
+ */
4
+ export declare enum LogLevel {
5
+ DEBUG = "DEBUG",
6
+ INFO = "INFO",
7
+ WARN = "WARN",
8
+ ERROR = "ERROR"
9
+ }
10
+ export declare class Logger {
11
+ private level;
12
+ setLevel(level: LogLevel): void;
13
+ debug(message: string, data?: any): void;
14
+ info(message: string, data?: any): void;
15
+ warn(message: string, data?: any): void;
16
+ error(message: string, error?: any): void;
17
+ private shouldLog;
18
+ }
19
+ export declare const logger: Logger;
20
+ //# sourceMappingURL=Logger.d.ts.map
@@ -0,0 +1,48 @@
1
+ "use strict";
2
+ /**
3
+ * 日志服务
4
+ */
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.logger = exports.Logger = exports.LogLevel = void 0;
7
+ var LogLevel;
8
+ (function (LogLevel) {
9
+ LogLevel["DEBUG"] = "DEBUG";
10
+ LogLevel["INFO"] = "INFO";
11
+ LogLevel["WARN"] = "WARN";
12
+ LogLevel["ERROR"] = "ERROR";
13
+ })(LogLevel || (exports.LogLevel = LogLevel = {}));
14
+ class Logger {
15
+ constructor() {
16
+ this.level = LogLevel.INFO;
17
+ }
18
+ setLevel(level) {
19
+ this.level = level;
20
+ }
21
+ debug(message, data) {
22
+ if (this.shouldLog(LogLevel.DEBUG)) {
23
+ console.log(`[DEBUG] ${message}`, data || '');
24
+ }
25
+ }
26
+ info(message, data) {
27
+ if (this.shouldLog(LogLevel.INFO)) {
28
+ console.log(`[INFO] ${message}`, data || '');
29
+ }
30
+ }
31
+ warn(message, data) {
32
+ if (this.shouldLog(LogLevel.WARN)) {
33
+ console.warn(`[WARN] ${message}`, data || '');
34
+ }
35
+ }
36
+ error(message, error) {
37
+ if (this.shouldLog(LogLevel.ERROR)) {
38
+ console.error(`[ERROR] ${message}`, error || '');
39
+ }
40
+ }
41
+ shouldLog(level) {
42
+ const levels = [LogLevel.DEBUG, LogLevel.INFO, LogLevel.WARN, LogLevel.ERROR];
43
+ return levels.indexOf(level) >= levels.indexOf(this.level);
44
+ }
45
+ }
46
+ exports.Logger = Logger;
47
+ exports.logger = new Logger();
48
+ //# sourceMappingURL=Logger.js.map
@@ -0,0 +1,12 @@
1
+ export type ProjectAssetStrategy = 'direct_copy' | 'template_generated' | 'runtime_generated';
2
+ export interface DirectCopyProjectAssetDefinition {
3
+ id: string;
4
+ category: 'tooling' | 'conventions' | 'ai_guidance' | 'hooks';
5
+ description: string;
6
+ targetRelativePath: string;
7
+ overwritePolicy: 'if_missing';
8
+ localizedSources?: Partial<Record<'zh-CN' | 'en-US', string>>;
9
+ sourceRelativePaths?: string[];
10
+ }
11
+ export declare const DIRECT_COPY_PROJECT_ASSETS: DirectCopyProjectAssetDefinition[];
12
+ //# sourceMappingURL=ProjectAssetRegistry.d.ts.map
@@ -0,0 +1,96 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DIRECT_COPY_PROJECT_ASSETS = void 0;
4
+ exports.DIRECT_COPY_PROJECT_ASSETS = [
5
+ {
6
+ id: 'ai-guide',
7
+ category: 'ai_guidance',
8
+ description: 'Project-adopted AI guide copied from the OSpec mother spec.',
9
+ targetRelativePath: 'for-ai/ai-guide.md',
10
+ overwritePolicy: 'if_missing',
11
+ localizedSources: {
12
+ 'zh-CN': 'assets/for-ai/zh-CN/ai-guide.md',
13
+ 'en-US': 'assets/for-ai/en-US/ai-guide.md',
14
+ },
15
+ },
16
+ {
17
+ id: 'execution-protocol',
18
+ category: 'ai_guidance',
19
+ description: 'Project-adopted execution protocol copied from the OSpec mother spec.',
20
+ targetRelativePath: 'for-ai/execution-protocol.md',
21
+ overwritePolicy: 'if_missing',
22
+ localizedSources: {
23
+ 'zh-CN': 'assets/for-ai/zh-CN/execution-protocol.md',
24
+ 'en-US': 'assets/for-ai/en-US/execution-protocol.md',
25
+ },
26
+ },
27
+ {
28
+ id: 'build-index-script',
29
+ category: 'tooling',
30
+ description: 'Rebuild script for SKILL.index.json.',
31
+ targetRelativePath: 'build-index-auto.cjs',
32
+ overwritePolicy: 'if_missing',
33
+ sourceRelativePaths: ['src/tools/build-index.js', 'dist/tools/build-index.js'],
34
+ },
35
+ {
36
+ id: 'project-naming-conventions',
37
+ category: 'conventions',
38
+ description: 'Project-adopted naming conventions copied from the OSpec mother spec.',
39
+ targetRelativePath: 'for-ai/naming-conventions.md',
40
+ overwritePolicy: 'if_missing',
41
+ localizedSources: {
42
+ 'zh-CN': 'assets/project-conventions/zh-CN/naming-conventions.md',
43
+ 'en-US': 'assets/project-conventions/en-US/naming-conventions.md',
44
+ },
45
+ },
46
+ {
47
+ id: 'project-skill-conventions',
48
+ category: 'conventions',
49
+ description: 'Project-adopted layered SKILL conventions copied from the OSpec mother spec.',
50
+ targetRelativePath: 'for-ai/skill-conventions.md',
51
+ overwritePolicy: 'if_missing',
52
+ localizedSources: {
53
+ 'zh-CN': 'assets/project-conventions/zh-CN/skill-conventions.md',
54
+ 'en-US': 'assets/project-conventions/en-US/skill-conventions.md',
55
+ },
56
+ },
57
+ {
58
+ id: 'project-development-guide',
59
+ category: 'conventions',
60
+ description: 'Project-adopted development guide copied from the OSpec mother spec.',
61
+ targetRelativePath: 'for-ai/development-guide.md',
62
+ overwritePolicy: 'if_missing',
63
+ localizedSources: {
64
+ 'zh-CN': 'assets/project-conventions/zh-CN/development-guide.md',
65
+ 'en-US': 'assets/project-conventions/en-US/development-guide.md',
66
+ },
67
+ },
68
+ {
69
+ id: 'project-workflow-conventions',
70
+ category: 'conventions',
71
+ description: 'Project-adopted workflow and change execution conventions copied from the OSpec mother spec.',
72
+ targetRelativePath: 'for-ai/workflow-conventions.md',
73
+ overwritePolicy: 'if_missing',
74
+ localizedSources: {
75
+ 'zh-CN': 'assets/project-conventions/zh-CN/workflow-conventions.md',
76
+ 'en-US': 'assets/project-conventions/en-US/workflow-conventions.md',
77
+ },
78
+ },
79
+ {
80
+ id: 'hook-template-pre-commit',
81
+ category: 'hooks',
82
+ description: 'Template for the OSpec pre-commit hook.',
83
+ targetRelativePath: '.ospec/templates/hooks/pre-commit',
84
+ overwritePolicy: 'if_missing',
85
+ sourceRelativePaths: ['assets/git-hooks/pre-commit'],
86
+ },
87
+ {
88
+ id: 'hook-template-post-merge',
89
+ category: 'hooks',
90
+ description: 'Template for the OSpec post-merge hook.',
91
+ targetRelativePath: '.ospec/templates/hooks/post-merge',
92
+ overwritePolicy: 'if_missing',
93
+ sourceRelativePaths: ['assets/git-hooks/post-merge'],
94
+ },
95
+ ];
96
+ //# sourceMappingURL=ProjectAssetRegistry.js.map
@@ -0,0 +1,49 @@
1
+ import { FileService } from './FileService';
2
+ import { DirectCopyProjectAssetDefinition } from './ProjectAssetRegistry';
3
+ interface AssetManifestOptions {
4
+ documentLanguage?: string;
5
+ templateGeneratedPaths: string[];
6
+ runtimeGeneratedPaths: string[];
7
+ }
8
+ export declare class ProjectAssetService {
9
+ private readonly fileService;
10
+ constructor(fileService: FileService);
11
+ getDirectCopyAssets(): DirectCopyProjectAssetDefinition[];
12
+ getDirectCopyTargetPaths(): string[];
13
+ getAssetPlan(documentLanguage?: string): {
14
+ directCopyFiles: string[];
15
+ templateGeneratedFiles: string[];
16
+ runtimeGeneratedFiles: string[];
17
+ localizedCopySources: Array<{
18
+ targetRelativePath: string;
19
+ sourceRelativePath: string;
20
+ }>;
21
+ };
22
+ installDirectCopyAssets(rootDir: string, documentLanguage?: string): Promise<{
23
+ created: string[];
24
+ skipped: string[];
25
+ }>;
26
+ syncDirectCopyAssets(rootDir: string, documentLanguage?: string, options?: {
27
+ targetRelativePaths?: string[];
28
+ }): Promise<{
29
+ created: string[];
30
+ refreshed: string[];
31
+ skipped: string[];
32
+ }>;
33
+ installGitHooks(rootDir: string, hookConfig?: {
34
+ 'pre-commit': boolean;
35
+ 'post-merge': boolean;
36
+ }): Promise<{
37
+ installed: string[];
38
+ skipped: string[];
39
+ }>;
40
+ writeAssetManifest(rootDir: string, options: AssetManifestOptions): Promise<void>;
41
+ private resolveSourceRelativePath;
42
+ private resolveStaticSourceHint;
43
+ private normalizePaths;
44
+ private getPackageRoot;
45
+ private isOSpecManagedHook;
46
+ }
47
+ export declare const createProjectAssetService: (fileService: FileService) => ProjectAssetService;
48
+ export {};
49
+ //# sourceMappingURL=ProjectAssetService.d.ts.map
@@ -0,0 +1,223 @@
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.createProjectAssetService = exports.ProjectAssetService = void 0;
7
+ const path_1 = __importDefault(require("path"));
8
+ const ProjectAssetRegistry_1 = require("./ProjectAssetRegistry");
9
+ class ProjectAssetService {
10
+ constructor(fileService) {
11
+ this.fileService = fileService;
12
+ }
13
+ getDirectCopyAssets() {
14
+ return ProjectAssetRegistry_1.DIRECT_COPY_PROJECT_ASSETS;
15
+ }
16
+ getDirectCopyTargetPaths() {
17
+ return this.getDirectCopyAssets().map(asset => asset.targetRelativePath);
18
+ }
19
+ getAssetPlan(documentLanguage) {
20
+ return {
21
+ directCopyFiles: this.getDirectCopyTargetPaths(),
22
+ templateGeneratedFiles: [],
23
+ runtimeGeneratedFiles: [],
24
+ localizedCopySources: this.getDirectCopyAssets().map(asset => ({
25
+ targetRelativePath: asset.targetRelativePath,
26
+ sourceRelativePath: this.resolveStaticSourceHint(asset, documentLanguage),
27
+ })),
28
+ };
29
+ }
30
+ async installDirectCopyAssets(rootDir, documentLanguage) {
31
+ const created = [];
32
+ const skipped = [];
33
+ for (const asset of this.getDirectCopyAssets()) {
34
+ const targetPath = path_1.default.join(rootDir, ...asset.targetRelativePath.split('/'));
35
+ if (await this.fileService.exists(targetPath)) {
36
+ skipped.push(asset.targetRelativePath);
37
+ continue;
38
+ }
39
+ const sourceRelativePath = await this.resolveSourceRelativePath(asset, documentLanguage);
40
+ const sourcePath = path_1.default.join(this.getPackageRoot(), ...sourceRelativePath.split('/'));
41
+ await this.fileService.copy(sourcePath, targetPath);
42
+ created.push(asset.targetRelativePath);
43
+ }
44
+ return { created, skipped };
45
+ }
46
+ async syncDirectCopyAssets(rootDir, documentLanguage, options = {}) {
47
+ const created = [];
48
+ const refreshed = [];
49
+ const skipped = [];
50
+ const targetFilter = Array.isArray(options.targetRelativePaths)
51
+ ? new Set(this.normalizePaths(options.targetRelativePaths))
52
+ : null;
53
+ for (const asset of this.getDirectCopyAssets()) {
54
+ if (targetFilter && !targetFilter.has(asset.targetRelativePath)) {
55
+ continue;
56
+ }
57
+ const targetPath = path_1.default.join(rootDir, ...asset.targetRelativePath.split('/'));
58
+ const sourceRelativePath = await this.resolveSourceRelativePath(asset, documentLanguage);
59
+ const sourcePath = path_1.default.join(this.getPackageRoot(), ...sourceRelativePath.split('/'));
60
+ const sourceContent = await this.fileService.readFile(sourcePath);
61
+ if (!(await this.fileService.exists(targetPath))) {
62
+ await this.fileService.writeFile(targetPath, sourceContent);
63
+ created.push(asset.targetRelativePath);
64
+ continue;
65
+ }
66
+ const targetContent = await this.fileService.readFile(targetPath);
67
+ if (targetContent === sourceContent) {
68
+ skipped.push(asset.targetRelativePath);
69
+ continue;
70
+ }
71
+ await this.fileService.writeFile(targetPath, sourceContent);
72
+ refreshed.push(asset.targetRelativePath);
73
+ }
74
+ return { created, refreshed, skipped };
75
+ }
76
+ async installGitHooks(rootDir, hookConfig) {
77
+ const installed = [];
78
+ const skipped = [];
79
+ const gitHooksDir = path_1.default.join(rootDir, '.git', 'hooks');
80
+ if (!(await this.fileService.exists(gitHooksDir))) {
81
+ return { installed, skipped };
82
+ }
83
+ const hooks = [
84
+ {
85
+ enabled: hookConfig?.['pre-commit'] !== false,
86
+ sourceRelativePath: '.ospec/templates/hooks/pre-commit',
87
+ targetRelativePath: '.git/hooks/pre-commit',
88
+ },
89
+ {
90
+ enabled: hookConfig?.['post-merge'] !== false,
91
+ sourceRelativePath: '.ospec/templates/hooks/post-merge',
92
+ targetRelativePath: '.git/hooks/post-merge',
93
+ },
94
+ ];
95
+ for (const hook of hooks) {
96
+ if (!hook.enabled) {
97
+ continue;
98
+ }
99
+ const sourcePath = path_1.default.join(rootDir, ...hook.sourceRelativePath.split('/'));
100
+ const targetPath = path_1.default.join(rootDir, ...hook.targetRelativePath.split('/'));
101
+ if (!(await this.fileService.exists(sourcePath))) {
102
+ skipped.push(hook.targetRelativePath);
103
+ continue;
104
+ }
105
+ const sourceContent = await this.fileService.readFile(sourcePath);
106
+ const targetExists = await this.fileService.exists(targetPath);
107
+ if (targetExists) {
108
+ const targetContent = await this.fileService.readFile(targetPath);
109
+ if (targetContent === sourceContent) {
110
+ skipped.push(hook.targetRelativePath);
111
+ continue;
112
+ }
113
+ if (!this.isOSpecManagedHook(targetContent)) {
114
+ skipped.push(hook.targetRelativePath);
115
+ continue;
116
+ }
117
+ }
118
+ await this.fileService.writeFile(targetPath, sourceContent);
119
+ installed.push(hook.targetRelativePath);
120
+ }
121
+ return { installed, skipped };
122
+ }
123
+ async writeAssetManifest(rootDir, options) {
124
+ const copyEntries = await Promise.all(this.getDirectCopyAssets().map(async (asset) => {
125
+ const targetPath = path_1.default.join(rootDir, ...asset.targetRelativePath.split('/'));
126
+ return {
127
+ id: asset.id,
128
+ strategy: 'direct_copy',
129
+ category: asset.category,
130
+ description: asset.description,
131
+ targetRelativePath: asset.targetRelativePath,
132
+ sourceRelativePath: await this.resolveSourceRelativePath(asset, options.documentLanguage),
133
+ overwritePolicy: asset.overwritePolicy,
134
+ exists: await this.fileService.exists(targetPath),
135
+ };
136
+ }));
137
+ const templateEntries = await Promise.all(this.normalizePaths(options.templateGeneratedPaths).map(async (targetRelativePath) => ({
138
+ id: `generated:${targetRelativePath}`,
139
+ strategy: 'template_generated',
140
+ category: 'templates',
141
+ description: 'Generated from OSpec template builders during project initialization.',
142
+ targetRelativePath,
143
+ sourceRelativePath: null,
144
+ overwritePolicy: 'if_missing',
145
+ exists: await this.fileService.exists(path_1.default.join(rootDir, ...targetRelativePath.split('/'))),
146
+ })));
147
+ const runtimeEntries = await Promise.all(this.normalizePaths(options.runtimeGeneratedPaths).map(async (targetRelativePath) => ({
148
+ id: `runtime:${targetRelativePath}`,
149
+ strategy: 'runtime_generated',
150
+ category: 'runtime',
151
+ description: 'Generated by OSpec at runtime or during index/config initialization.',
152
+ targetRelativePath,
153
+ sourceRelativePath: null,
154
+ overwritePolicy: 'rebuild',
155
+ exists: targetRelativePath === '.ospec/asset-sources.json'
156
+ ? true
157
+ : await this.fileService.exists(path_1.default.join(rootDir, ...targetRelativePath.split('/'))),
158
+ })));
159
+ const assets = [...copyEntries, ...templateEntries, ...runtimeEntries];
160
+ const manifestAsset = assets.find(asset => asset.targetRelativePath === '.ospec/asset-sources.json');
161
+ if (manifestAsset) {
162
+ manifestAsset.exists = true;
163
+ }
164
+ const manifest = {
165
+ version: '1.0',
166
+ generatedAt: new Date().toISOString(),
167
+ documentLanguage: options.documentLanguage || 'zh-CN',
168
+ assets,
169
+ summary: {
170
+ directCopy: copyEntries.length,
171
+ templateGenerated: templateEntries.length,
172
+ runtimeGenerated: runtimeEntries.length,
173
+ },
174
+ };
175
+ await this.fileService.writeJSON(path_1.default.join(rootDir, '.ospec', 'asset-sources.json'), manifest);
176
+ }
177
+ async resolveSourceRelativePath(asset, documentLanguage) {
178
+ const candidates = [];
179
+ if (documentLanguage && asset.localizedSources?.[documentLanguage]) {
180
+ candidates.push(asset.localizedSources[documentLanguage]);
181
+ }
182
+ if (asset.localizedSources?.['zh-CN']) {
183
+ candidates.push(asset.localizedSources['zh-CN']);
184
+ }
185
+ if (asset.sourceRelativePaths) {
186
+ candidates.push(...asset.sourceRelativePaths);
187
+ }
188
+ for (const candidate of candidates) {
189
+ const absolutePath = path_1.default.join(this.getPackageRoot(), ...candidate.split('/'));
190
+ if (await this.fileService.exists(absolutePath)) {
191
+ return candidate;
192
+ }
193
+ }
194
+ throw new Error(`Unable to resolve packaged project asset: ${asset.id}`);
195
+ }
196
+ resolveStaticSourceHint(asset, documentLanguage) {
197
+ if (documentLanguage && asset.localizedSources?.[documentLanguage]) {
198
+ return asset.localizedSources[documentLanguage];
199
+ }
200
+ if (asset.localizedSources?.['zh-CN']) {
201
+ return asset.localizedSources['zh-CN'];
202
+ }
203
+ if (asset.sourceRelativePaths?.[0]) {
204
+ return asset.sourceRelativePaths[0];
205
+ }
206
+ return '';
207
+ }
208
+ normalizePaths(paths) {
209
+ return Array.from(new Set(paths
210
+ .map(item => item.replace(/\\/g, '/'))
211
+ .filter(Boolean))).sort((left, right) => left.localeCompare(right));
212
+ }
213
+ getPackageRoot() {
214
+ return path_1.default.resolve(__dirname, '../..');
215
+ }
216
+ isOSpecManagedHook(content) {
217
+ return content.includes('build-index-auto.cjs') || content.includes('build-index-auto.js') || content.includes('[ospec]');
218
+ }
219
+ }
220
+ exports.ProjectAssetService = ProjectAssetService;
221
+ const createProjectAssetService = (fileService) => new ProjectAssetService(fileService);
222
+ exports.createProjectAssetService = createProjectAssetService;
223
+ //# sourceMappingURL=ProjectAssetService.js.map
@@ -0,0 +1,73 @@
1
+ import { FileService } from './FileService';
2
+ import { Logger } from './Logger';
3
+ import { NormalizedProjectBootstrapInput } from './TemplateEngine';
4
+ import { ProjectScaffoldPlan } from './ProjectScaffoldService';
5
+ export interface ProjectScaffoldCommandStep {
6
+ id: string;
7
+ title: string;
8
+ command: string;
9
+ args: string[];
10
+ shellCommand: string;
11
+ description: string;
12
+ phase: 'install';
13
+ }
14
+ export interface ProjectScaffoldCommandPlan {
15
+ presetId: string;
16
+ autoExecute: boolean;
17
+ steps: ProjectScaffoldCommandStep[];
18
+ deferredMessage: string;
19
+ }
20
+ export interface ProjectScaffoldCommandStepResult {
21
+ id: string;
22
+ title: string;
23
+ shellCommand: string;
24
+ status: 'completed' | 'failed' | 'skipped';
25
+ exitCode: number | null;
26
+ durationMs: number;
27
+ stdoutSnippet: string;
28
+ stderrSnippet: string;
29
+ }
30
+ export interface ProjectScaffoldCommandExecutionResult {
31
+ status: 'skipped' | 'completed' | 'failed';
32
+ steps: ProjectScaffoldCommandStepResult[];
33
+ recoveryFilePath: string | null;
34
+ }
35
+ export interface ProjectBootstrapRecoveryRecord {
36
+ generatedAt: string;
37
+ projectPresetId: string | null;
38
+ projectName: string;
39
+ failedStep: {
40
+ id: string;
41
+ title: string;
42
+ shellCommand: string;
43
+ };
44
+ createdArtifacts: {
45
+ scaffoldFiles: string[];
46
+ scaffoldDirectories: string[];
47
+ directCopyFiles: string[];
48
+ hooks: string[];
49
+ };
50
+ remediation: string[];
51
+ }
52
+ interface RecoveryInput {
53
+ normalized: NormalizedProjectBootstrapInput;
54
+ failedStep: ProjectScaffoldCommandStepResult;
55
+ scaffoldCreatedFiles: string[];
56
+ scaffoldCreatedDirectories: string[];
57
+ directCopyCreatedFiles: string[];
58
+ hookInstalledFiles: string[];
59
+ }
60
+ export declare class ProjectScaffoldCommandService {
61
+ private readonly fileService;
62
+ private readonly logger;
63
+ constructor(fileService: FileService, logger: Logger);
64
+ getPlan(normalized: NormalizedProjectBootstrapInput, scaffoldPlan: ProjectScaffoldPlan | null): ProjectScaffoldCommandPlan | null;
65
+ executePlan(rootDir: string, plan: ProjectScaffoldCommandPlan | null): Promise<ProjectScaffoldCommandExecutionResult>;
66
+ writeRecoveryRecord(rootDir: string, input: RecoveryInput): Promise<string>;
67
+ private executeStep;
68
+ private resolvePackageManagerRunner;
69
+ private limitOutput;
70
+ }
71
+ export declare const createProjectScaffoldCommandService: (fileService: FileService, logger: Logger) => ProjectScaffoldCommandService;
72
+ export {};
73
+ //# sourceMappingURL=ProjectScaffoldCommandService.d.ts.map