@dynamicworks/br-openspec 1.3.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 (291) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +210 -0
  3. package/README.pt-BR.md +212 -0
  4. package/bin/openspec.js +3 -0
  5. package/dist/cli/index.d.ts +2 -0
  6. package/dist/cli/index.js +484 -0
  7. package/dist/commands/change.d.ts +35 -0
  8. package/dist/commands/change.js +278 -0
  9. package/dist/commands/completion.d.ts +72 -0
  10. package/dist/commands/completion.js +258 -0
  11. package/dist/commands/config.d.ts +36 -0
  12. package/dist/commands/config.js +553 -0
  13. package/dist/commands/feedback.d.ts +9 -0
  14. package/dist/commands/feedback.js +184 -0
  15. package/dist/commands/schema.d.ts +6 -0
  16. package/dist/commands/schema.js +869 -0
  17. package/dist/commands/show.d.ts +14 -0
  18. package/dist/commands/show.js +133 -0
  19. package/dist/commands/spec.d.ts +15 -0
  20. package/dist/commands/spec.js +226 -0
  21. package/dist/commands/tools.d.ts +11 -0
  22. package/dist/commands/tools.js +252 -0
  23. package/dist/commands/validate.d.ts +24 -0
  24. package/dist/commands/validate.js +295 -0
  25. package/dist/commands/workflow/index.d.ts +17 -0
  26. package/dist/commands/workflow/index.js +12 -0
  27. package/dist/commands/workflow/instructions.d.ts +29 -0
  28. package/dist/commands/workflow/instructions.js +328 -0
  29. package/dist/commands/workflow/new-change.d.ts +11 -0
  30. package/dist/commands/workflow/new-change.js +44 -0
  31. package/dist/commands/workflow/schemas.d.ts +10 -0
  32. package/dist/commands/workflow/schemas.js +35 -0
  33. package/dist/commands/workflow/shared.d.ts +57 -0
  34. package/dist/commands/workflow/shared.js +117 -0
  35. package/dist/commands/workflow/status.d.ts +14 -0
  36. package/dist/commands/workflow/status.js +76 -0
  37. package/dist/commands/workflow/templates.d.ts +16 -0
  38. package/dist/commands/workflow/templates.js +70 -0
  39. package/dist/core/archive.d.ts +11 -0
  40. package/dist/core/archive.js +322 -0
  41. package/dist/core/artifact-graph/graph.d.ts +56 -0
  42. package/dist/core/artifact-graph/graph.js +141 -0
  43. package/dist/core/artifact-graph/index.d.ts +8 -0
  44. package/dist/core/artifact-graph/index.js +14 -0
  45. package/dist/core/artifact-graph/instruction-loader.d.ts +143 -0
  46. package/dist/core/artifact-graph/instruction-loader.js +217 -0
  47. package/dist/core/artifact-graph/outputs.d.ts +14 -0
  48. package/dist/core/artifact-graph/outputs.js +39 -0
  49. package/dist/core/artifact-graph/resolver.d.ts +81 -0
  50. package/dist/core/artifact-graph/resolver.js +258 -0
  51. package/dist/core/artifact-graph/schema.d.ts +13 -0
  52. package/dist/core/artifact-graph/schema.js +108 -0
  53. package/dist/core/artifact-graph/state.d.ts +12 -0
  54. package/dist/core/artifact-graph/state.js +31 -0
  55. package/dist/core/artifact-graph/types.d.ts +45 -0
  56. package/dist/core/artifact-graph/types.js +43 -0
  57. package/dist/core/available-tools.d.ts +17 -0
  58. package/dist/core/available-tools.js +43 -0
  59. package/dist/core/command-generation/adapters/amazon-q.d.ts +13 -0
  60. package/dist/core/command-generation/adapters/amazon-q.js +26 -0
  61. package/dist/core/command-generation/adapters/antigravity.d.ts +13 -0
  62. package/dist/core/command-generation/adapters/antigravity.js +26 -0
  63. package/dist/core/command-generation/adapters/auggie.d.ts +13 -0
  64. package/dist/core/command-generation/adapters/auggie.js +27 -0
  65. package/dist/core/command-generation/adapters/bob.d.ts +14 -0
  66. package/dist/core/command-generation/adapters/bob.js +45 -0
  67. package/dist/core/command-generation/adapters/claude.d.ts +13 -0
  68. package/dist/core/command-generation/adapters/claude.js +50 -0
  69. package/dist/core/command-generation/adapters/cline.d.ts +14 -0
  70. package/dist/core/command-generation/adapters/cline.js +27 -0
  71. package/dist/core/command-generation/adapters/codebuddy.d.ts +13 -0
  72. package/dist/core/command-generation/adapters/codebuddy.js +28 -0
  73. package/dist/core/command-generation/adapters/codex.d.ts +16 -0
  74. package/dist/core/command-generation/adapters/codex.js +39 -0
  75. package/dist/core/command-generation/adapters/continue.d.ts +13 -0
  76. package/dist/core/command-generation/adapters/continue.js +28 -0
  77. package/dist/core/command-generation/adapters/costrict.d.ts +13 -0
  78. package/dist/core/command-generation/adapters/costrict.js +27 -0
  79. package/dist/core/command-generation/adapters/crush.d.ts +13 -0
  80. package/dist/core/command-generation/adapters/crush.js +30 -0
  81. package/dist/core/command-generation/adapters/cursor.d.ts +14 -0
  82. package/dist/core/command-generation/adapters/cursor.js +44 -0
  83. package/dist/core/command-generation/adapters/factory.d.ts +13 -0
  84. package/dist/core/command-generation/adapters/factory.js +27 -0
  85. package/dist/core/command-generation/adapters/gemini.d.ts +13 -0
  86. package/dist/core/command-generation/adapters/gemini.js +26 -0
  87. package/dist/core/command-generation/adapters/github-copilot.d.ts +13 -0
  88. package/dist/core/command-generation/adapters/github-copilot.js +26 -0
  89. package/dist/core/command-generation/adapters/iflow.d.ts +13 -0
  90. package/dist/core/command-generation/adapters/iflow.js +29 -0
  91. package/dist/core/command-generation/adapters/index.d.ts +32 -0
  92. package/dist/core/command-generation/adapters/index.js +32 -0
  93. package/dist/core/command-generation/adapters/junie.d.ts +13 -0
  94. package/dist/core/command-generation/adapters/junie.js +26 -0
  95. package/dist/core/command-generation/adapters/kilocode.d.ts +14 -0
  96. package/dist/core/command-generation/adapters/kilocode.js +23 -0
  97. package/dist/core/command-generation/adapters/kiro.d.ts +13 -0
  98. package/dist/core/command-generation/adapters/kiro.js +26 -0
  99. package/dist/core/command-generation/adapters/lingma.d.ts +13 -0
  100. package/dist/core/command-generation/adapters/lingma.js +30 -0
  101. package/dist/core/command-generation/adapters/opencode.d.ts +13 -0
  102. package/dist/core/command-generation/adapters/opencode.js +29 -0
  103. package/dist/core/command-generation/adapters/pi.d.ts +18 -0
  104. package/dist/core/command-generation/adapters/pi.js +55 -0
  105. package/dist/core/command-generation/adapters/qoder.d.ts +13 -0
  106. package/dist/core/command-generation/adapters/qoder.js +30 -0
  107. package/dist/core/command-generation/adapters/qwen.d.ts +13 -0
  108. package/dist/core/command-generation/adapters/qwen.js +26 -0
  109. package/dist/core/command-generation/adapters/roocode.d.ts +14 -0
  110. package/dist/core/command-generation/adapters/roocode.js +27 -0
  111. package/dist/core/command-generation/adapters/windsurf.d.ts +14 -0
  112. package/dist/core/command-generation/adapters/windsurf.js +51 -0
  113. package/dist/core/command-generation/generator.d.ts +21 -0
  114. package/dist/core/command-generation/generator.js +27 -0
  115. package/dist/core/command-generation/index.d.ts +22 -0
  116. package/dist/core/command-generation/index.js +24 -0
  117. package/dist/core/command-generation/registry.d.ts +36 -0
  118. package/dist/core/command-generation/registry.js +98 -0
  119. package/dist/core/command-generation/types.d.ts +56 -0
  120. package/dist/core/command-generation/types.js +8 -0
  121. package/dist/core/completions/command-registry.d.ts +7 -0
  122. package/dist/core/completions/command-registry.js +462 -0
  123. package/dist/core/completions/completion-provider.d.ts +60 -0
  124. package/dist/core/completions/completion-provider.js +102 -0
  125. package/dist/core/completions/factory.d.ts +64 -0
  126. package/dist/core/completions/factory.js +75 -0
  127. package/dist/core/completions/generators/bash-generator.d.ts +32 -0
  128. package/dist/core/completions/generators/bash-generator.js +174 -0
  129. package/dist/core/completions/generators/fish-generator.d.ts +32 -0
  130. package/dist/core/completions/generators/fish-generator.js +157 -0
  131. package/dist/core/completions/generators/powershell-generator.d.ts +33 -0
  132. package/dist/core/completions/generators/powershell-generator.js +208 -0
  133. package/dist/core/completions/generators/zsh-generator.d.ts +44 -0
  134. package/dist/core/completions/generators/zsh-generator.js +250 -0
  135. package/dist/core/completions/installers/bash-installer.d.ts +87 -0
  136. package/dist/core/completions/installers/bash-installer.js +319 -0
  137. package/dist/core/completions/installers/fish-installer.d.ts +43 -0
  138. package/dist/core/completions/installers/fish-installer.js +143 -0
  139. package/dist/core/completions/installers/powershell-installer.d.ts +102 -0
  140. package/dist/core/completions/installers/powershell-installer.js +400 -0
  141. package/dist/core/completions/installers/zsh-installer.d.ts +125 -0
  142. package/dist/core/completions/installers/zsh-installer.js +450 -0
  143. package/dist/core/completions/templates/bash-templates.d.ts +6 -0
  144. package/dist/core/completions/templates/bash-templates.js +24 -0
  145. package/dist/core/completions/templates/fish-templates.d.ts +7 -0
  146. package/dist/core/completions/templates/fish-templates.js +39 -0
  147. package/dist/core/completions/templates/powershell-templates.d.ts +6 -0
  148. package/dist/core/completions/templates/powershell-templates.js +25 -0
  149. package/dist/core/completions/templates/zsh-templates.d.ts +6 -0
  150. package/dist/core/completions/templates/zsh-templates.js +36 -0
  151. package/dist/core/completions/types.d.ts +79 -0
  152. package/dist/core/completions/types.js +2 -0
  153. package/dist/core/config-prompts.d.ts +9 -0
  154. package/dist/core/config-prompts.js +34 -0
  155. package/dist/core/config-schema.d.ts +86 -0
  156. package/dist/core/config-schema.js +213 -0
  157. package/dist/core/config.d.ts +18 -0
  158. package/dist/core/config.js +38 -0
  159. package/dist/core/converters/json-converter.d.ts +6 -0
  160. package/dist/core/converters/json-converter.js +51 -0
  161. package/dist/core/global-config.d.ts +44 -0
  162. package/dist/core/global-config.js +125 -0
  163. package/dist/core/index.d.ts +2 -0
  164. package/dist/core/index.js +3 -0
  165. package/dist/core/init.d.ts +37 -0
  166. package/dist/core/init.js +549 -0
  167. package/dist/core/is-project-initialized.d.ts +12 -0
  168. package/dist/core/is-project-initialized.js +18 -0
  169. package/dist/core/legacy-cleanup.d.ts +162 -0
  170. package/dist/core/legacy-cleanup.js +515 -0
  171. package/dist/core/list.d.ts +9 -0
  172. package/dist/core/list.js +172 -0
  173. package/dist/core/migration.d.ts +23 -0
  174. package/dist/core/migration.js +109 -0
  175. package/dist/core/parsers/change-parser.d.ts +13 -0
  176. package/dist/core/parsers/change-parser.js +197 -0
  177. package/dist/core/parsers/markdown-parser.d.ts +26 -0
  178. package/dist/core/parsers/markdown-parser.js +228 -0
  179. package/dist/core/parsers/requirement-blocks.d.ts +37 -0
  180. package/dist/core/parsers/requirement-blocks.js +201 -0
  181. package/dist/core/parsers/spec-structure.d.ts +9 -0
  182. package/dist/core/parsers/spec-structure.js +88 -0
  183. package/dist/core/profile-sync-drift.d.ts +38 -0
  184. package/dist/core/profile-sync-drift.js +200 -0
  185. package/dist/core/profiles.d.ts +26 -0
  186. package/dist/core/profiles.js +40 -0
  187. package/dist/core/project-config.d.ts +64 -0
  188. package/dist/core/project-config.js +224 -0
  189. package/dist/core/schemas/base.schema.d.ts +13 -0
  190. package/dist/core/schemas/base.schema.js +13 -0
  191. package/dist/core/schemas/change.schema.d.ts +73 -0
  192. package/dist/core/schemas/change.schema.js +31 -0
  193. package/dist/core/schemas/index.d.ts +4 -0
  194. package/dist/core/schemas/index.js +4 -0
  195. package/dist/core/schemas/spec.schema.d.ts +18 -0
  196. package/dist/core/schemas/spec.schema.js +15 -0
  197. package/dist/core/shared/index.d.ts +8 -0
  198. package/dist/core/shared/index.js +8 -0
  199. package/dist/core/shared/skill-generation.d.ts +49 -0
  200. package/dist/core/shared/skill-generation.js +96 -0
  201. package/dist/core/shared/tool-detection.d.ts +71 -0
  202. package/dist/core/shared/tool-detection.js +158 -0
  203. package/dist/core/specs-apply.d.ts +73 -0
  204. package/dist/core/specs-apply.js +393 -0
  205. package/dist/core/styles/palette.d.ts +7 -0
  206. package/dist/core/styles/palette.js +8 -0
  207. package/dist/core/templates/index.d.ts +8 -0
  208. package/dist/core/templates/index.js +9 -0
  209. package/dist/core/templates/skill-templates.d.ts +20 -0
  210. package/dist/core/templates/skill-templates.js +19 -0
  211. package/dist/core/templates/types.d.ts +19 -0
  212. package/dist/core/templates/types.js +5 -0
  213. package/dist/core/templates/workflows/apply-change.d.ts +10 -0
  214. package/dist/core/templates/workflows/apply-change.js +308 -0
  215. package/dist/core/templates/workflows/archive-change.d.ts +10 -0
  216. package/dist/core/templates/workflows/archive-change.js +271 -0
  217. package/dist/core/templates/workflows/bulk-archive-change.d.ts +10 -0
  218. package/dist/core/templates/workflows/bulk-archive-change.js +492 -0
  219. package/dist/core/templates/workflows/continue-change.d.ts +10 -0
  220. package/dist/core/templates/workflows/continue-change.js +232 -0
  221. package/dist/core/templates/workflows/explore.d.ts +10 -0
  222. package/dist/core/templates/workflows/explore.js +463 -0
  223. package/dist/core/templates/workflows/feedback.d.ts +9 -0
  224. package/dist/core/templates/workflows/feedback.js +108 -0
  225. package/dist/core/templates/workflows/ff-change.d.ts +10 -0
  226. package/dist/core/templates/workflows/ff-change.js +198 -0
  227. package/dist/core/templates/workflows/new-change.d.ts +10 -0
  228. package/dist/core/templates/workflows/new-change.js +21 -0
  229. package/dist/core/templates/workflows/onboard.d.ts +10 -0
  230. package/dist/core/templates/workflows/onboard.js +21 -0
  231. package/dist/core/templates/workflows/propose.d.ts +10 -0
  232. package/dist/core/templates/workflows/propose.js +216 -0
  233. package/dist/core/templates/workflows/sync-specs.d.ts +10 -0
  234. package/dist/core/templates/workflows/sync-specs.js +272 -0
  235. package/dist/core/templates/workflows/upstream-sync.d.ts +10 -0
  236. package/dist/core/templates/workflows/upstream-sync.js +116 -0
  237. package/dist/core/templates/workflows/verify-change.d.ts +10 -0
  238. package/dist/core/templates/workflows/verify-change.js +21 -0
  239. package/dist/core/tools-manager.d.ts +56 -0
  240. package/dist/core/tools-manager.js +215 -0
  241. package/dist/core/update.d.ts +77 -0
  242. package/dist/core/update.js +538 -0
  243. package/dist/core/validation/constants.d.ts +34 -0
  244. package/dist/core/validation/constants.js +40 -0
  245. package/dist/core/validation/types.d.ts +18 -0
  246. package/dist/core/validation/types.js +2 -0
  247. package/dist/core/validation/validator.d.ts +33 -0
  248. package/dist/core/validation/validator.js +419 -0
  249. package/dist/core/view.d.ts +8 -0
  250. package/dist/core/view.js +169 -0
  251. package/dist/index.d.ts +3 -0
  252. package/dist/index.js +3 -0
  253. package/dist/messages/index.d.ts +867 -0
  254. package/dist/messages/index.js +1960 -0
  255. package/dist/prompts/searchable-multi-select.d.ts +28 -0
  256. package/dist/prompts/searchable-multi-select.js +160 -0
  257. package/dist/telemetry/config.d.ts +38 -0
  258. package/dist/telemetry/config.js +136 -0
  259. package/dist/telemetry/index.d.ts +31 -0
  260. package/dist/telemetry/index.js +165 -0
  261. package/dist/ui/ascii-patterns.d.ts +16 -0
  262. package/dist/ui/ascii-patterns.js +133 -0
  263. package/dist/ui/welcome-screen.d.ts +10 -0
  264. package/dist/ui/welcome-screen.js +147 -0
  265. package/dist/utils/change-metadata.d.ts +51 -0
  266. package/dist/utils/change-metadata.js +147 -0
  267. package/dist/utils/change-utils.d.ts +62 -0
  268. package/dist/utils/change-utils.js +121 -0
  269. package/dist/utils/command-references.d.ts +18 -0
  270. package/dist/utils/command-references.js +20 -0
  271. package/dist/utils/file-system.d.ts +41 -0
  272. package/dist/utils/file-system.js +302 -0
  273. package/dist/utils/index.d.ts +6 -0
  274. package/dist/utils/index.js +9 -0
  275. package/dist/utils/interactive.d.ts +18 -0
  276. package/dist/utils/interactive.js +21 -0
  277. package/dist/utils/item-discovery.d.ts +4 -0
  278. package/dist/utils/item-discovery.js +72 -0
  279. package/dist/utils/match.d.ts +3 -0
  280. package/dist/utils/match.js +22 -0
  281. package/dist/utils/shell-detection.d.ts +20 -0
  282. package/dist/utils/shell-detection.js +41 -0
  283. package/dist/utils/task-progress.d.ts +8 -0
  284. package/dist/utils/task-progress.js +37 -0
  285. package/package.json +84 -0
  286. package/schemas/spec-driven/schema.yaml +153 -0
  287. package/schemas/spec-driven/templates/design.md +19 -0
  288. package/schemas/spec-driven/templates/proposal.md +23 -0
  289. package/schemas/spec-driven/templates/spec.md +8 -0
  290. package/schemas/spec-driven/templates/tasks.md +9 -0
  291. package/scripts/postinstall.js +83 -0
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Init Command
3
+ *
4
+ * Sets up OpenSpec with Agent Skills and /opsx:* slash commands.
5
+ * This is the unified setup command that replaces both the old init and experimental commands.
6
+ */
7
+ type InitCommandOptions = {
8
+ tools?: string;
9
+ force?: boolean;
10
+ interactive?: boolean;
11
+ profile?: string;
12
+ };
13
+ export declare class InitCommand {
14
+ private readonly toolsArg?;
15
+ private readonly force;
16
+ private readonly interactiveOption?;
17
+ private readonly profileOverride?;
18
+ constructor(options?: InitCommandOptions);
19
+ execute(targetPath: string): Promise<void>;
20
+ private validate;
21
+ private canPromptInteractively;
22
+ private resolveProfileOverride;
23
+ private handleLegacyCleanup;
24
+ private performLegacyCleanup;
25
+ private getSelectedTools;
26
+ private resolveToolsArg;
27
+ private validateTools;
28
+ private createDirectoryStructure;
29
+ private generateSkillsAndCommands;
30
+ private createConfig;
31
+ private displaySuccessMessage;
32
+ private startSpinner;
33
+ private removeSkillDirs;
34
+ private removeCommandFiles;
35
+ }
36
+ export {};
37
+ //# sourceMappingURL=init.d.ts.map
@@ -0,0 +1,549 @@
1
+ /**
2
+ * Init Command
3
+ *
4
+ * Sets up OpenSpec with Agent Skills and /opsx:* slash commands.
5
+ * This is the unified setup command that replaces both the old init and experimental commands.
6
+ */
7
+ import path from 'path';
8
+ import chalk from 'chalk';
9
+ import ora from 'ora';
10
+ import * as fs from 'fs';
11
+ import { createRequire } from 'module';
12
+ import { FileSystemUtils } from '../utils/file-system.js';
13
+ import { transformToHyphenCommands } from '../utils/command-references.js';
14
+ import { AI_TOOLS, OPENSPEC_DIR_NAME, } from './config.js';
15
+ import { PALETTE } from './styles/palette.js';
16
+ import { isInteractive } from '../utils/interactive.js';
17
+ import { INIT_MESSAGES } from '../messages/index.js';
18
+ import { serializeConfig } from './config-prompts.js';
19
+ import { generateCommands, CommandAdapterRegistry, } from './command-generation/index.js';
20
+ import { removeOpenSpecSkillDirs, removeOpenSpecCommandFiles, } from './tools-manager.js';
21
+ import { detectLegacyArtifacts, cleanupLegacyArtifacts, formatCleanupSummary, formatDetectionSummary, } from './legacy-cleanup.js';
22
+ import { getToolsWithSkillsDir, getToolStates, getSkillTemplates, getCommandContents, generateSkillContent, } from './shared/index.js';
23
+ import { getGlobalConfig } from './global-config.js';
24
+ import { getProfileWorkflows } from './profiles.js';
25
+ import { getAvailableTools } from './available-tools.js';
26
+ import { migrateIfNeeded } from './migration.js';
27
+ const require = createRequire(import.meta.url);
28
+ const { version: OPENSPEC_VERSION } = require('../../package.json');
29
+ // -----------------------------------------------------------------------------
30
+ // Constants
31
+ // -----------------------------------------------------------------------------
32
+ const DEFAULT_SCHEMA = 'spec-driven';
33
+ const PROGRESS_SPINNER = {
34
+ interval: 80,
35
+ frames: ['░░░', '▒░░', '▒▒░', '▒▒▒', '▓▒▒', '▓▓▒', '▓▓▓', '▒▓▓', '░▒▓'],
36
+ };
37
+ // -----------------------------------------------------------------------------
38
+ // Init Command Class
39
+ // -----------------------------------------------------------------------------
40
+ export class InitCommand {
41
+ toolsArg;
42
+ force;
43
+ interactiveOption;
44
+ profileOverride;
45
+ constructor(options = {}) {
46
+ this.toolsArg = options.tools;
47
+ this.force = options.force ?? false;
48
+ this.interactiveOption = options.interactive;
49
+ this.profileOverride = options.profile;
50
+ }
51
+ async execute(targetPath) {
52
+ const projectPath = path.resolve(targetPath);
53
+ const openspecDir = OPENSPEC_DIR_NAME;
54
+ const openspecPath = path.join(projectPath, openspecDir);
55
+ // Validation happens silently in the background
56
+ const extendMode = await this.validate(projectPath, openspecPath);
57
+ // Check for legacy artifacts and handle cleanup
58
+ await this.handleLegacyCleanup(projectPath, extendMode);
59
+ // Detect available tools in the project (task 7.1)
60
+ const detectedTools = getAvailableTools(projectPath);
61
+ // Migration check: migrate existing projects to profile system (task 7.3)
62
+ if (extendMode) {
63
+ migrateIfNeeded(projectPath, detectedTools);
64
+ }
65
+ // Show animated welcome screen (interactive mode only)
66
+ const canPrompt = this.canPromptInteractively();
67
+ if (canPrompt) {
68
+ const { showWelcomeScreen } = await import('../ui/welcome-screen.js');
69
+ await showWelcomeScreen();
70
+ }
71
+ // Validate profile override early so invalid values fail before tool setup.
72
+ // The resolved value is consumed later when generation reads effective config.
73
+ this.resolveProfileOverride();
74
+ // Get tool states before processing
75
+ const toolStates = getToolStates(projectPath);
76
+ // Get tool selection (pass detected tools for pre-selection)
77
+ const selectedToolIds = await this.getSelectedTools(toolStates, extendMode, detectedTools, projectPath);
78
+ // Validate selected tools
79
+ const validatedTools = this.validateTools(selectedToolIds, toolStates);
80
+ // Create directory structure and config
81
+ await this.createDirectoryStructure(openspecPath, extendMode);
82
+ // Generate skills and commands for each tool
83
+ const results = await this.generateSkillsAndCommands(projectPath, validatedTools);
84
+ // Create config.yaml if needed
85
+ const configStatus = await this.createConfig(openspecPath, extendMode);
86
+ // Display success message
87
+ this.displaySuccessMessage(projectPath, validatedTools, results, configStatus);
88
+ }
89
+ // ═══════════════════════════════════════════════════════════
90
+ // VALIDATION & SETUP
91
+ // ═══════════════════════════════════════════════════════════
92
+ async validate(projectPath, openspecPath) {
93
+ const extendMode = await FileSystemUtils.directoryExists(openspecPath);
94
+ // Check write permissions
95
+ if (!(await FileSystemUtils.ensureWritePermissions(projectPath))) {
96
+ throw new Error(INIT_MESSAGES.insufficientPermissions(projectPath));
97
+ }
98
+ return extendMode;
99
+ }
100
+ canPromptInteractively() {
101
+ if (this.interactiveOption === false)
102
+ return false;
103
+ if (this.toolsArg !== undefined)
104
+ return false;
105
+ return isInteractive({ interactive: this.interactiveOption });
106
+ }
107
+ resolveProfileOverride() {
108
+ if (this.profileOverride === undefined) {
109
+ return undefined;
110
+ }
111
+ if (this.profileOverride === 'core' || this.profileOverride === 'custom') {
112
+ return this.profileOverride;
113
+ }
114
+ throw new Error(INIT_MESSAGES.invalidProfile(this.profileOverride));
115
+ }
116
+ // ═══════════════════════════════════════════════════════════
117
+ // LEGACY CLEANUP
118
+ // ═══════════════════════════════════════════════════════════
119
+ async handleLegacyCleanup(projectPath, extendMode) {
120
+ // Detect legacy artifacts
121
+ const detection = await detectLegacyArtifacts(projectPath);
122
+ if (!detection.hasLegacyArtifacts) {
123
+ return; // No legacy artifacts found
124
+ }
125
+ // Show what was detected
126
+ console.log();
127
+ console.log(formatDetectionSummary(detection));
128
+ console.log();
129
+ const canPrompt = this.canPromptInteractively();
130
+ if (this.force || !canPrompt) {
131
+ // --force flag or non-interactive mode: proceed with cleanup automatically.
132
+ // Legacy slash commands are 100% OpenSpec-managed, and config file cleanup
133
+ // only removes markers (never deletes files), so auto-cleanup is safe.
134
+ await this.performLegacyCleanup(projectPath, detection);
135
+ return;
136
+ }
137
+ // Interactive mode: prompt for confirmation
138
+ const { confirm } = await import('@inquirer/prompts');
139
+ const shouldCleanup = await confirm({
140
+ message: INIT_MESSAGES.upgradeLegacyPrompt,
141
+ default: true,
142
+ });
143
+ if (!shouldCleanup) {
144
+ console.log(chalk.dim(INIT_MESSAGES.initializationCancelled));
145
+ console.log(chalk.dim(INIT_MESSAGES.skipPromptHint));
146
+ process.exit(0);
147
+ }
148
+ await this.performLegacyCleanup(projectPath, detection);
149
+ }
150
+ async performLegacyCleanup(projectPath, detection) {
151
+ const spinner = ora(INIT_MESSAGES.cleaningLegacy).start();
152
+ const result = await cleanupLegacyArtifacts(projectPath, detection);
153
+ spinner.succeed(INIT_MESSAGES.legacyCleaned);
154
+ const summary = formatCleanupSummary(result);
155
+ if (summary) {
156
+ console.log();
157
+ console.log(summary);
158
+ }
159
+ console.log();
160
+ }
161
+ // ═══════════════════════════════════════════════════════════
162
+ // TOOL SELECTION
163
+ // ═══════════════════════════════════════════════════════════
164
+ async getSelectedTools(toolStates, extendMode, detectedTools, projectPath) {
165
+ // Check for --tools flag first
166
+ const nonInteractiveSelection = this.resolveToolsArg();
167
+ if (nonInteractiveSelection !== null) {
168
+ return nonInteractiveSelection;
169
+ }
170
+ const validTools = getToolsWithSkillsDir();
171
+ const detectedToolIds = new Set(detectedTools.map((t) => t.value));
172
+ const configuredToolIds = new Set([...toolStates.entries()]
173
+ .filter(([, status]) => status.configured)
174
+ .map(([toolId]) => toolId));
175
+ const shouldPreselectDetected = !extendMode && configuredToolIds.size === 0;
176
+ const canPrompt = this.canPromptInteractively();
177
+ // Non-interactive mode: use detected tools as fallback (task 7.8)
178
+ if (!canPrompt) {
179
+ if (detectedToolIds.size > 0) {
180
+ return [...detectedToolIds];
181
+ }
182
+ throw new Error(INIT_MESSAGES.noToolsDetected(validTools.join('\n ')));
183
+ }
184
+ if (validTools.length === 0) {
185
+ throw new Error(INIT_MESSAGES.noToolsAvailable);
186
+ }
187
+ // Interactive mode: show searchable multi-select
188
+ const { searchableMultiSelect } = await import('../prompts/searchable-multi-select.js');
189
+ // Build choices: pre-select configured tools; keep detected tools visible but unselected.
190
+ const sortedChoices = validTools
191
+ .map((toolId) => {
192
+ const tool = AI_TOOLS.find((t) => t.value === toolId);
193
+ const status = toolStates.get(toolId);
194
+ const configured = status?.configured ?? false;
195
+ const detected = detectedToolIds.has(toolId);
196
+ return {
197
+ name: tool?.name || toolId,
198
+ value: toolId,
199
+ configured,
200
+ detected: detected && !configured,
201
+ preSelected: configured || (shouldPreselectDetected && detected && !configured),
202
+ };
203
+ })
204
+ .sort((a, b) => {
205
+ // Configured tools first, then detected (not configured), then everything else.
206
+ if (a.configured && !b.configured)
207
+ return -1;
208
+ if (!a.configured && b.configured)
209
+ return 1;
210
+ if (a.detected && !b.detected)
211
+ return -1;
212
+ if (!a.detected && b.detected)
213
+ return 1;
214
+ return 0;
215
+ });
216
+ const configuredNames = validTools
217
+ .filter((toolId) => configuredToolIds.has(toolId))
218
+ .map((toolId) => AI_TOOLS.find((t) => t.value === toolId)?.name || toolId);
219
+ if (configuredNames.length > 0) {
220
+ console.log(INIT_MESSAGES.configuredPreselected(configuredNames.join(', ')));
221
+ }
222
+ const detectedOnlyNames = detectedTools
223
+ .filter((tool) => !configuredToolIds.has(tool.value))
224
+ .map((tool) => tool.name);
225
+ if (detectedOnlyNames.length > 0) {
226
+ const detectionLabel = shouldPreselectDetected
227
+ ? INIT_MESSAGES.preselectedFirstTime
228
+ : INIT_MESSAGES.notPreselected;
229
+ console.log(INIT_MESSAGES.detectedToolsLabel(detectedOnlyNames.join(', '), detectionLabel));
230
+ }
231
+ const selectedTools = await searchableMultiSelect({
232
+ message: INIT_MESSAGES.selectToolsPrompt(validTools.length),
233
+ pageSize: 15,
234
+ choices: sortedChoices,
235
+ validate: (selected) => selected.length > 0 || INIT_MESSAGES.selectAtLeastOneTool,
236
+ });
237
+ if (selectedTools.length === 0) {
238
+ throw new Error(INIT_MESSAGES.atLeastOneToolRequired);
239
+ }
240
+ return selectedTools;
241
+ }
242
+ resolveToolsArg() {
243
+ if (typeof this.toolsArg === 'undefined') {
244
+ return null;
245
+ }
246
+ const raw = this.toolsArg.trim();
247
+ if (raw.length === 0) {
248
+ throw new Error(INIT_MESSAGES.toolsOptionRequired);
249
+ }
250
+ const availableTools = getToolsWithSkillsDir();
251
+ const availableSet = new Set(availableTools);
252
+ const availableList = ['all', 'none', ...availableTools].join(', ');
253
+ const lowerRaw = raw.toLowerCase();
254
+ if (lowerRaw === 'all') {
255
+ return availableTools;
256
+ }
257
+ if (lowerRaw === 'none') {
258
+ return [];
259
+ }
260
+ const tokens = raw
261
+ .split(',')
262
+ .map((token) => token.trim())
263
+ .filter((token) => token.length > 0);
264
+ if (tokens.length === 0) {
265
+ throw new Error(INIT_MESSAGES.toolsOptionRequiresToolId);
266
+ }
267
+ const normalizedTokens = tokens.map((token) => token.toLowerCase());
268
+ if (normalizedTokens.some((token) => token === 'all' || token === 'none')) {
269
+ throw new Error(INIT_MESSAGES.cannotCombineReservedValues);
270
+ }
271
+ const invalidTokens = tokens.filter((_token, index) => !availableSet.has(normalizedTokens[index]));
272
+ if (invalidTokens.length > 0) {
273
+ throw new Error(INIT_MESSAGES.invalidTools(invalidTokens.join(', '), availableList));
274
+ }
275
+ // Deduplicate while preserving order
276
+ const deduped = [];
277
+ for (const token of normalizedTokens) {
278
+ if (!deduped.includes(token)) {
279
+ deduped.push(token);
280
+ }
281
+ }
282
+ return deduped;
283
+ }
284
+ validateTools(toolIds, toolStates) {
285
+ const validatedTools = [];
286
+ for (const toolId of toolIds) {
287
+ const tool = AI_TOOLS.find((t) => t.value === toolId);
288
+ if (!tool) {
289
+ const validToolIds = getToolsWithSkillsDir();
290
+ throw new Error(INIT_MESSAGES.unknownTool(toolId, validToolIds.join('\n ')));
291
+ }
292
+ if (!tool.skillsDir) {
293
+ const validToolsWithSkills = getToolsWithSkillsDir();
294
+ throw new Error(INIT_MESSAGES.toolNoSkillSupport(toolId, validToolsWithSkills.join('\n ')));
295
+ }
296
+ const preState = toolStates.get(tool.value);
297
+ validatedTools.push({
298
+ value: tool.value,
299
+ name: tool.name,
300
+ skillsDir: tool.skillsDir,
301
+ wasConfigured: preState?.configured ?? false,
302
+ });
303
+ }
304
+ return validatedTools;
305
+ }
306
+ // ═══════════════════════════════════════════════════════════
307
+ // DIRECTORY STRUCTURE
308
+ // ═══════════════════════════════════════════════════════════
309
+ async createDirectoryStructure(openspecPath, extendMode) {
310
+ if (extendMode) {
311
+ // In extend mode, just ensure directories exist without spinner
312
+ const directories = [
313
+ openspecPath,
314
+ path.join(openspecPath, 'specs'),
315
+ path.join(openspecPath, 'changes'),
316
+ path.join(openspecPath, 'changes', 'archive'),
317
+ ];
318
+ for (const dir of directories) {
319
+ await FileSystemUtils.createDirectory(dir);
320
+ }
321
+ return;
322
+ }
323
+ const spinner = this.startSpinner(INIT_MESSAGES.creatingStructure);
324
+ const directories = [
325
+ openspecPath,
326
+ path.join(openspecPath, 'specs'),
327
+ path.join(openspecPath, 'changes'),
328
+ path.join(openspecPath, 'changes', 'archive'),
329
+ ];
330
+ for (const dir of directories) {
331
+ await FileSystemUtils.createDirectory(dir);
332
+ }
333
+ spinner.stopAndPersist({
334
+ symbol: PALETTE.white('▌'),
335
+ text: PALETTE.white(INIT_MESSAGES.structureCreated),
336
+ });
337
+ }
338
+ // ═══════════════════════════════════════════════════════════
339
+ // SKILL & COMMAND GENERATION
340
+ // ═══════════════════════════════════════════════════════════
341
+ async generateSkillsAndCommands(projectPath, tools) {
342
+ const createdTools = [];
343
+ const refreshedTools = [];
344
+ const failedTools = [];
345
+ const commandsSkipped = [];
346
+ let removedCommandCount = 0;
347
+ let removedSkillCount = 0;
348
+ // Read global config for profile and delivery settings (use --profile override if set)
349
+ const globalConfig = getGlobalConfig();
350
+ const profile = this.resolveProfileOverride() ?? globalConfig.profile ?? 'core';
351
+ const delivery = globalConfig.delivery ?? 'both';
352
+ const workflows = getProfileWorkflows(profile, globalConfig.workflows);
353
+ // Get skill and command templates filtered by profile workflows
354
+ const shouldGenerateSkills = delivery !== 'commands';
355
+ const shouldGenerateCommands = delivery !== 'skills';
356
+ const skillTemplates = shouldGenerateSkills ? getSkillTemplates(workflows) : [];
357
+ const commandContents = shouldGenerateCommands ? getCommandContents(workflows) : [];
358
+ // Process each tool
359
+ for (const tool of tools) {
360
+ const spinner = ora(INIT_MESSAGES.settingUp(tool.name)).start();
361
+ try {
362
+ // Generate skill files if delivery includes skills
363
+ if (shouldGenerateSkills) {
364
+ // Use tool-specific skillsDir
365
+ const skillsDir = path.join(projectPath, tool.skillsDir, 'skills');
366
+ // Create skill directories and SKILL.md files
367
+ for (const { template, dirName } of skillTemplates) {
368
+ const skillDir = path.join(skillsDir, dirName);
369
+ const skillFile = path.join(skillDir, 'SKILL.md');
370
+ // Generate SKILL.md content with YAML frontmatter including generatedBy
371
+ // Use hyphen-based command references for tools where filename = command name
372
+ const transformer = (tool.value === 'opencode' || tool.value === 'pi') ? transformToHyphenCommands : undefined;
373
+ const skillContent = generateSkillContent(template, OPENSPEC_VERSION, transformer);
374
+ // Write the skill file
375
+ await FileSystemUtils.writeFile(skillFile, skillContent);
376
+ }
377
+ }
378
+ if (!shouldGenerateSkills) {
379
+ const skillsDir = path.join(projectPath, tool.skillsDir, 'skills');
380
+ removedSkillCount += await this.removeSkillDirs(skillsDir);
381
+ }
382
+ // Generate commands if delivery includes commands
383
+ if (shouldGenerateCommands) {
384
+ const adapter = CommandAdapterRegistry.get(tool.value);
385
+ if (adapter) {
386
+ const generatedCommands = generateCommands(commandContents, adapter);
387
+ for (const cmd of generatedCommands) {
388
+ const commandFile = path.isAbsolute(cmd.path) ? cmd.path : path.join(projectPath, cmd.path);
389
+ await FileSystemUtils.writeFile(commandFile, cmd.fileContent);
390
+ }
391
+ }
392
+ else {
393
+ commandsSkipped.push(tool.value);
394
+ }
395
+ }
396
+ if (!shouldGenerateCommands) {
397
+ removedCommandCount += await this.removeCommandFiles(projectPath, tool.value);
398
+ }
399
+ spinner.succeed(INIT_MESSAGES.setupComplete(tool.name));
400
+ if (tool.wasConfigured) {
401
+ refreshedTools.push(tool);
402
+ }
403
+ else {
404
+ createdTools.push(tool);
405
+ }
406
+ }
407
+ catch (error) {
408
+ spinner.fail(INIT_MESSAGES.setupFailed(tool.name));
409
+ failedTools.push({ name: tool.name, error: error });
410
+ }
411
+ }
412
+ return {
413
+ createdTools,
414
+ refreshedTools,
415
+ failedTools,
416
+ commandsSkipped,
417
+ removedCommandCount,
418
+ removedSkillCount,
419
+ };
420
+ }
421
+ // ═══════════════════════════════════════════════════════════
422
+ // CONFIG FILE
423
+ // ═══════════════════════════════════════════════════════════
424
+ async createConfig(openspecPath, extendMode) {
425
+ const configPath = path.join(openspecPath, 'config.yaml');
426
+ const configYmlPath = path.join(openspecPath, 'config.yml');
427
+ const configYamlExists = fs.existsSync(configPath);
428
+ const configYmlExists = fs.existsSync(configYmlPath);
429
+ if (configYamlExists || configYmlExists) {
430
+ return 'exists';
431
+ }
432
+ // In non-interactive mode without --force, skip config creation
433
+ if (!this.canPromptInteractively() && !this.force) {
434
+ return 'skipped';
435
+ }
436
+ try {
437
+ const yamlContent = serializeConfig({ schema: DEFAULT_SCHEMA });
438
+ await FileSystemUtils.writeFile(configPath, yamlContent);
439
+ return 'created';
440
+ }
441
+ catch {
442
+ return 'skipped';
443
+ }
444
+ }
445
+ // ═══════════════════════════════════════════════════════════
446
+ // UI & OUTPUT
447
+ // ═══════════════════════════════════════════════════════════
448
+ displaySuccessMessage(projectPath, tools, results, configStatus) {
449
+ console.log();
450
+ console.log(chalk.bold(INIT_MESSAGES.setupCompleteTitle));
451
+ console.log();
452
+ // Show created vs refreshed tools
453
+ if (results.createdTools.length > 0) {
454
+ console.log(INIT_MESSAGES.created(results.createdTools.map((t) => t.name).join(', ')));
455
+ }
456
+ if (results.refreshedTools.length > 0) {
457
+ console.log(INIT_MESSAGES.refreshed(results.refreshedTools.map((t) => t.name).join(', ')));
458
+ }
459
+ // Show counts (respecting profile filter)
460
+ const successfulTools = [...results.createdTools, ...results.refreshedTools];
461
+ if (successfulTools.length > 0) {
462
+ const globalConfig = getGlobalConfig();
463
+ const profile = this.profileOverride ?? globalConfig.profile ?? 'core';
464
+ const delivery = globalConfig.delivery ?? 'both';
465
+ const workflows = getProfileWorkflows(profile, globalConfig.workflows);
466
+ const toolDirs = [...new Set(successfulTools.map((t) => t.skillsDir))].join(', ');
467
+ const skillCount = delivery !== 'commands' ? getSkillTemplates(workflows).length : 0;
468
+ const commandCount = delivery !== 'skills' ? getCommandContents(workflows).length : 0;
469
+ if (skillCount > 0 && commandCount > 0) {
470
+ console.log(INIT_MESSAGES.skillsAndCommandsCount(skillCount, commandCount, toolDirs));
471
+ }
472
+ else if (skillCount > 0) {
473
+ console.log(INIT_MESSAGES.skillsCount(skillCount, toolDirs));
474
+ }
475
+ else if (commandCount > 0) {
476
+ console.log(INIT_MESSAGES.commandsCount(commandCount, toolDirs));
477
+ }
478
+ }
479
+ // Show failures
480
+ if (results.failedTools.length > 0) {
481
+ console.log(chalk.red(INIT_MESSAGES.failed(results.failedTools.map((f) => `${f.name} (${f.error.message})`).join(', '))));
482
+ }
483
+ // Show skipped commands
484
+ if (results.commandsSkipped.length > 0) {
485
+ console.log(chalk.dim(INIT_MESSAGES.commandsSkipped(results.commandsSkipped.join(', '))));
486
+ }
487
+ if (results.removedCommandCount > 0) {
488
+ console.log(chalk.dim(INIT_MESSAGES.removedCommands(results.removedCommandCount)));
489
+ }
490
+ if (results.removedSkillCount > 0) {
491
+ console.log(chalk.dim(INIT_MESSAGES.removedSkills(results.removedSkillCount)));
492
+ }
493
+ // Config status
494
+ if (configStatus === 'created') {
495
+ console.log(INIT_MESSAGES.configCreated(DEFAULT_SCHEMA));
496
+ }
497
+ else if (configStatus === 'exists') {
498
+ // Show actual filename (config.yaml or config.yml)
499
+ const configYaml = path.join(projectPath, OPENSPEC_DIR_NAME, 'config.yaml');
500
+ const configYml = path.join(projectPath, OPENSPEC_DIR_NAME, 'config.yml');
501
+ const configName = fs.existsSync(configYaml) ? 'config.yaml' : fs.existsSync(configYml) ? 'config.yml' : 'config.yaml';
502
+ console.log(INIT_MESSAGES.configExists(configName));
503
+ }
504
+ else {
505
+ console.log(chalk.dim(INIT_MESSAGES.configSkipped));
506
+ }
507
+ // Getting started (task 7.6: show propose if in profile)
508
+ const globalCfg = getGlobalConfig();
509
+ const activeProfile = this.profileOverride ?? globalCfg.profile ?? 'core';
510
+ const activeWorkflows = [...getProfileWorkflows(activeProfile, globalCfg.workflows)];
511
+ console.log();
512
+ if (activeWorkflows.includes('propose')) {
513
+ console.log(chalk.bold(INIT_MESSAGES.gettingStarted));
514
+ console.log(INIT_MESSAGES.startFirstChangePropose('/opsx:propose "sua ideia"'));
515
+ }
516
+ else if (activeWorkflows.includes('new')) {
517
+ console.log(chalk.bold(INIT_MESSAGES.gettingStarted));
518
+ console.log(INIT_MESSAGES.startFirstChangeNew('/opsx:new "sua ideia"'));
519
+ }
520
+ else {
521
+ console.log(INIT_MESSAGES.configureWorkflowsHint);
522
+ }
523
+ // Links
524
+ console.log();
525
+ console.log(INIT_MESSAGES.learnMore(chalk.cyan('https://github.com/dynamicworks-com-br/BR-OpenSpec')));
526
+ console.log(INIT_MESSAGES.feedback(chalk.cyan('https://github.com/dynamicworks-com-br/BR-OpenSpec/issues')));
527
+ // Restart instruction if any tools were configured
528
+ if (results.createdTools.length > 0 || results.refreshedTools.length > 0) {
529
+ console.log();
530
+ console.log(chalk.white(INIT_MESSAGES.restartIDE));
531
+ }
532
+ console.log();
533
+ }
534
+ startSpinner(text) {
535
+ return ora({
536
+ text,
537
+ stream: process.stdout,
538
+ color: 'gray',
539
+ spinner: PROGRESS_SPINNER,
540
+ }).start();
541
+ }
542
+ async removeSkillDirs(skillsDir) {
543
+ return removeOpenSpecSkillDirs(skillsDir);
544
+ }
545
+ async removeCommandFiles(projectPath, toolId) {
546
+ return removeOpenSpecCommandFiles(projectPath, toolId);
547
+ }
548
+ }
549
+ //# sourceMappingURL=init.js.map
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Project Initialization Check
3
+ *
4
+ * Utility for checking whether a project has been initialized with OpenSpec.
5
+ * Used to guard commands that require an already-initialized project.
6
+ */
7
+ /**
8
+ * Returns true if the project at the given path has been initialized with
9
+ * OpenSpec (i.e., `openspec/config.yaml` or `openspec/config.yml` exists).
10
+ */
11
+ export declare function isProjectInitialized(projectPath: string): boolean;
12
+ //# sourceMappingURL=is-project-initialized.d.ts.map
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Project Initialization Check
3
+ *
4
+ * Utility for checking whether a project has been initialized with OpenSpec.
5
+ * Used to guard commands that require an already-initialized project.
6
+ */
7
+ import { existsSync } from 'fs';
8
+ import path from 'path';
9
+ /**
10
+ * Returns true if the project at the given path has been initialized with
11
+ * OpenSpec (i.e., `openspec/config.yaml` or `openspec/config.yml` exists).
12
+ */
13
+ export function isProjectInitialized(projectPath) {
14
+ const base = path.join(projectPath, 'openspec');
15
+ return (existsSync(path.join(base, 'config.yaml')) ||
16
+ existsSync(path.join(base, 'config.yml')));
17
+ }
18
+ //# sourceMappingURL=is-project-initialized.js.map