@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,319 @@
1
+ import { promises as fs } from 'fs';
2
+ import path from 'path';
3
+ import os from 'os';
4
+ import { FileSystemUtils } from '../../../utils/file-system.js';
5
+ import { COMPLETION_MESSAGES } from '../../../messages/index.js';
6
+ /**
7
+ * Installer for Bash completion scripts.
8
+ * Supports bash-completion package and standalone installations.
9
+ */
10
+ export class BashInstaller {
11
+ homeDir;
12
+ /**
13
+ * Markers for .bashrc configuration management
14
+ */
15
+ BASHRC_MARKERS = {
16
+ start: '# OPENSPEC:START',
17
+ end: '# OPENSPEC:END',
18
+ };
19
+ constructor(homeDir = os.homedir()) {
20
+ this.homeDir = homeDir;
21
+ }
22
+ /**
23
+ * Check if bash-completion is installed
24
+ *
25
+ * @returns true if bash-completion directories exist
26
+ */
27
+ async isBashCompletionInstalled() {
28
+ const paths = [
29
+ '/usr/share/bash-completion', // Linux system-wide
30
+ '/usr/local/share/bash-completion', // Homebrew Intel (main)
31
+ '/opt/homebrew/etc/bash_completion.d', // Homebrew Apple Silicon
32
+ '/usr/local/etc/bash_completion.d', // Homebrew Intel (alt path)
33
+ '/etc/bash_completion.d', // Legacy fallback
34
+ ];
35
+ for (const p of paths) {
36
+ try {
37
+ const stat = await fs.stat(p);
38
+ if (stat.isDirectory()) {
39
+ return true;
40
+ }
41
+ }
42
+ catch {
43
+ // Continue checking other paths
44
+ }
45
+ }
46
+ return false;
47
+ }
48
+ /**
49
+ * Get the appropriate installation path for the completion script
50
+ *
51
+ * @returns Installation path
52
+ */
53
+ async getInstallationPath() {
54
+ // Try user-local bash-completion directory first
55
+ const localCompletionDir = path.join(this.homeDir, '.local', 'share', 'bash-completion', 'completions');
56
+ // For user installation, use local directory
57
+ return path.join(localCompletionDir, 'openspec');
58
+ }
59
+ /**
60
+ * Backup an existing completion file if it exists
61
+ *
62
+ * @param targetPath - Path to the file to backup
63
+ * @returns Path to the backup file, or undefined if no backup was needed
64
+ */
65
+ async backupExistingFile(targetPath) {
66
+ try {
67
+ await fs.access(targetPath);
68
+ // File exists, create a backup
69
+ const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
70
+ const backupPath = `${targetPath}.backup-${timestamp}`;
71
+ await fs.copyFile(targetPath, backupPath);
72
+ return backupPath;
73
+ }
74
+ catch {
75
+ // File doesn't exist, no backup needed
76
+ return undefined;
77
+ }
78
+ }
79
+ /**
80
+ * Get the path to .bashrc file
81
+ *
82
+ * @returns Path to .bashrc
83
+ */
84
+ getBashrcPath() {
85
+ return path.join(this.homeDir, '.bashrc');
86
+ }
87
+ /**
88
+ * Generate .bashrc configuration content
89
+ *
90
+ * @param completionsDir - Directory containing completion scripts
91
+ * @returns Configuration content
92
+ */
93
+ generateBashrcConfig(completionsDir) {
94
+ return [
95
+ '# BR-OpenSpec shell completions configuration',
96
+ `if [ -d "${completionsDir}" ]; then`,
97
+ ` for f in "${completionsDir}"/*; do`,
98
+ ' [ -f "$f" ] && . "$f"',
99
+ ' done',
100
+ 'fi',
101
+ ].join('\n');
102
+ }
103
+ /**
104
+ * Configure .bashrc to enable completions
105
+ *
106
+ * @param completionsDir - Directory containing completion scripts
107
+ * @returns true if configured successfully, false otherwise
108
+ */
109
+ async configureBashrc(completionsDir) {
110
+ // Check if auto-configuration is disabled
111
+ if (process.env.OPENSPEC_NO_AUTO_CONFIG === '1') {
112
+ return false;
113
+ }
114
+ try {
115
+ const bashrcPath = this.getBashrcPath();
116
+ const config = this.generateBashrcConfig(completionsDir);
117
+ // Check write permissions
118
+ const canWrite = await FileSystemUtils.canWriteFile(bashrcPath);
119
+ if (!canWrite) {
120
+ return false;
121
+ }
122
+ // Use marker-based update
123
+ await FileSystemUtils.updateFileWithMarkers(bashrcPath, config, this.BASHRC_MARKERS.start, this.BASHRC_MARKERS.end);
124
+ return true;
125
+ }
126
+ catch (error) {
127
+ // Fail gracefully - don't break installation
128
+ console.debug(`Unable to configure .bashrc for completions: ${error.message}`);
129
+ return false;
130
+ }
131
+ }
132
+ /**
133
+ * Remove .bashrc configuration
134
+ * Used during uninstallation
135
+ *
136
+ * @returns true if removed successfully, false otherwise
137
+ */
138
+ async removeBashrcConfig() {
139
+ try {
140
+ const bashrcPath = this.getBashrcPath();
141
+ // Check if file exists
142
+ try {
143
+ await fs.access(bashrcPath);
144
+ }
145
+ catch {
146
+ // File doesn't exist, nothing to remove
147
+ return true;
148
+ }
149
+ // Read file content
150
+ const content = await fs.readFile(bashrcPath, 'utf-8');
151
+ // Check if markers exist
152
+ if (!content.includes(this.BASHRC_MARKERS.start) || !content.includes(this.BASHRC_MARKERS.end)) {
153
+ // Markers don't exist, nothing to remove
154
+ return true;
155
+ }
156
+ // Remove content between markers (including markers)
157
+ const lines = content.split('\n');
158
+ const startIndex = lines.findIndex((line) => line.trim() === this.BASHRC_MARKERS.start);
159
+ const endIndex = lines.findIndex((line) => line.trim() === this.BASHRC_MARKERS.end);
160
+ if (startIndex === -1 || endIndex === -1 || endIndex < startIndex) {
161
+ // Invalid marker placement
162
+ return false;
163
+ }
164
+ // Remove lines between markers (inclusive)
165
+ lines.splice(startIndex, endIndex - startIndex + 1);
166
+ // Remove trailing empty lines
167
+ while (lines.length > 0 && lines[lines.length - 1].trim() === '') {
168
+ lines.pop();
169
+ }
170
+ // Write back
171
+ await fs.writeFile(bashrcPath, lines.join('\n'), 'utf-8');
172
+ return true;
173
+ }
174
+ catch (error) {
175
+ // Fail gracefully
176
+ console.debug(`Unable to remove .bashrc configuration: ${error.message}`);
177
+ return false;
178
+ }
179
+ }
180
+ /**
181
+ * Install the completion script
182
+ *
183
+ * @param completionScript - The completion script content to install
184
+ * @returns Installation result with status and instructions
185
+ */
186
+ async install(completionScript) {
187
+ try {
188
+ const targetPath = await this.getInstallationPath();
189
+ // Check for bash-completion package
190
+ const hasBashCompletion = await this.isBashCompletionInstalled();
191
+ // Check if already installed with same content
192
+ let isUpdate = false;
193
+ try {
194
+ const existingContent = await fs.readFile(targetPath, 'utf-8');
195
+ if (existingContent === completionScript) {
196
+ // Already installed and up to date
197
+ return {
198
+ success: true,
199
+ installedPath: targetPath,
200
+ message: COMPLETION_MESSAGES.bashAlreadyInstalled,
201
+ instructions: [
202
+ COMPLETION_MESSAGES.bashAlreadyInstalledDetail,
203
+ COMPLETION_MESSAGES.bashAlreadyInstalledHint,
204
+ ],
205
+ };
206
+ }
207
+ // File exists but content is different - this is an update
208
+ isUpdate = true;
209
+ }
210
+ catch (error) {
211
+ // File doesn't exist or can't be read, proceed with installation
212
+ console.debug(`Unable to read existing completion file at ${targetPath}: ${error.message}`);
213
+ }
214
+ // Ensure the directory exists
215
+ const targetDir = path.dirname(targetPath);
216
+ await fs.mkdir(targetDir, { recursive: true });
217
+ // Backup existing file if updating
218
+ const backupPath = isUpdate ? await this.backupExistingFile(targetPath) : undefined;
219
+ // Write the completion script
220
+ await fs.writeFile(targetPath, completionScript, 'utf-8');
221
+ // Auto-configure .bashrc
222
+ const bashrcConfigured = await this.configureBashrc(targetDir);
223
+ // Generate instructions if .bashrc wasn't auto-configured
224
+ const instructions = bashrcConfigured ? undefined : this.generateInstructions(targetPath);
225
+ // Collect warnings
226
+ const warnings = [];
227
+ if (!hasBashCompletion) {
228
+ warnings.push(COMPLETION_MESSAGES.bashCompletionNotDetected, '', COMPLETION_MESSAGES.bashCompletionRequired, COMPLETION_MESSAGES.installWith, ' brew install bash-completion@2', '', COMPLETION_MESSAGES.addToBashProfile, ' [[ -r "/opt/homebrew/etc/profile.d/bash_completion.sh" ]] && . "/opt/homebrew/etc/profile.d/bash_completion.sh"');
229
+ }
230
+ // Determine appropriate message
231
+ let message;
232
+ if (isUpdate) {
233
+ message = backupPath
234
+ ? COMPLETION_MESSAGES.bashUpdatedWithBackup
235
+ : COMPLETION_MESSAGES.bashUpdated;
236
+ }
237
+ else {
238
+ message = bashrcConfigured
239
+ ? COMPLETION_MESSAGES.bashInstalledWithBashrc
240
+ : COMPLETION_MESSAGES.bashInstalled;
241
+ }
242
+ return {
243
+ success: true,
244
+ installedPath: targetPath,
245
+ backupPath,
246
+ bashrcConfigured,
247
+ message,
248
+ instructions,
249
+ warnings: warnings.length > 0 ? warnings : undefined,
250
+ };
251
+ }
252
+ catch (error) {
253
+ return {
254
+ success: false,
255
+ message: COMPLETION_MESSAGES.bashFailedToInstall(error instanceof Error ? error.message : String(error)),
256
+ };
257
+ }
258
+ }
259
+ /**
260
+ * Generate user instructions for enabling completions
261
+ *
262
+ * @param installedPath - Path where the script was installed
263
+ * @returns Array of instruction strings
264
+ */
265
+ generateInstructions(installedPath) {
266
+ const completionsDir = path.dirname(installedPath);
267
+ return [
268
+ COMPLETION_MESSAGES.bashScriptInstalled,
269
+ '',
270
+ COMPLETION_MESSAGES.bashAddToBashrc,
271
+ '',
272
+ ` ${COMPLETION_MESSAGES.bashSourceComment}`,
273
+ ` if [ -d "${completionsDir}" ]; then`,
274
+ ` for f in "${completionsDir}"/*; do`,
275
+ ' [ -f "$f" ] && . "$f"',
276
+ ' done',
277
+ ' fi',
278
+ '',
279
+ COMPLETION_MESSAGES.bashThenRestartShell('exec bash'),
280
+ ];
281
+ }
282
+ /**
283
+ * Uninstall the completion script
284
+ *
285
+ * @param options - Optional uninstall options
286
+ * @param options.yes - Skip confirmation prompt (handled by command layer)
287
+ * @returns Uninstallation result
288
+ */
289
+ async uninstall(options) {
290
+ try {
291
+ const targetPath = await this.getInstallationPath();
292
+ // Check if installed
293
+ try {
294
+ await fs.access(targetPath);
295
+ }
296
+ catch {
297
+ return {
298
+ success: false,
299
+ message: COMPLETION_MESSAGES.bashNotInstalled,
300
+ };
301
+ }
302
+ // Remove the completion script
303
+ await fs.unlink(targetPath);
304
+ // Remove .bashrc configuration
305
+ await this.removeBashrcConfig();
306
+ return {
307
+ success: true,
308
+ message: COMPLETION_MESSAGES.bashUninstalled,
309
+ };
310
+ }
311
+ catch (error) {
312
+ return {
313
+ success: false,
314
+ message: COMPLETION_MESSAGES.bashFailedToUninstall(error instanceof Error ? error.message : String(error)),
315
+ };
316
+ }
317
+ }
318
+ }
319
+ //# sourceMappingURL=bash-installer.js.map
@@ -0,0 +1,43 @@
1
+ import { InstallationResult } from '../factory.js';
2
+ /**
3
+ * Installer for Fish completion scripts.
4
+ * Fish automatically loads completions from ~/.config/fish/completions/
5
+ */
6
+ export declare class FishInstaller {
7
+ private readonly homeDir;
8
+ constructor(homeDir?: string);
9
+ /**
10
+ * Get the installation path for Fish completions
11
+ *
12
+ * @returns Installation path
13
+ */
14
+ getInstallationPath(): string;
15
+ /**
16
+ * Backup an existing completion file if it exists
17
+ *
18
+ * @param targetPath - Path to the file to backup
19
+ * @returns Path to the backup file, or undefined if no backup was needed
20
+ */
21
+ backupExistingFile(targetPath: string): Promise<string | undefined>;
22
+ /**
23
+ * Install the completion script
24
+ *
25
+ * @param completionScript - The completion script content to install
26
+ * @returns Installation result with status and instructions
27
+ */
28
+ install(completionScript: string): Promise<InstallationResult>;
29
+ /**
30
+ * Uninstall the completion script
31
+ *
32
+ * @param options - Optional uninstall options
33
+ * @param options.yes - Skip confirmation prompt (handled by command layer)
34
+ * @returns Uninstallation result
35
+ */
36
+ uninstall(options?: {
37
+ yes?: boolean;
38
+ }): Promise<{
39
+ success: boolean;
40
+ message: string;
41
+ }>;
42
+ }
43
+ //# sourceMappingURL=fish-installer.d.ts.map
@@ -0,0 +1,143 @@
1
+ import { promises as fs } from 'fs';
2
+ import path from 'path';
3
+ import os from 'os';
4
+ /**
5
+ * Installer for Fish completion scripts.
6
+ * Fish automatically loads completions from ~/.config/fish/completions/
7
+ */
8
+ export class FishInstaller {
9
+ homeDir;
10
+ constructor(homeDir = os.homedir()) {
11
+ this.homeDir = homeDir;
12
+ }
13
+ /**
14
+ * Get the installation path for Fish completions
15
+ *
16
+ * @returns Installation path
17
+ */
18
+ getInstallationPath() {
19
+ return path.join(this.homeDir, '.config', 'fish', 'completions', 'openspec.fish');
20
+ }
21
+ /**
22
+ * Backup an existing completion file if it exists
23
+ *
24
+ * @param targetPath - Path to the file to backup
25
+ * @returns Path to the backup file, or undefined if no backup was needed
26
+ */
27
+ async backupExistingFile(targetPath) {
28
+ try {
29
+ await fs.access(targetPath);
30
+ // File exists, create a backup
31
+ const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
32
+ const backupPath = `${targetPath}.backup-${timestamp}`;
33
+ await fs.copyFile(targetPath, backupPath);
34
+ return backupPath;
35
+ }
36
+ catch {
37
+ // File doesn't exist, no backup needed
38
+ return undefined;
39
+ }
40
+ }
41
+ /**
42
+ * Install the completion script
43
+ *
44
+ * @param completionScript - The completion script content to install
45
+ * @returns Installation result with status and instructions
46
+ */
47
+ async install(completionScript) {
48
+ try {
49
+ const targetPath = this.getInstallationPath();
50
+ // Check if already installed with same content
51
+ let isUpdate = false;
52
+ try {
53
+ const existingContent = await fs.readFile(targetPath, 'utf-8');
54
+ if (existingContent === completionScript) {
55
+ // Already installed and up to date
56
+ return {
57
+ success: true,
58
+ installedPath: targetPath,
59
+ message: 'Completion script is already installed (up to date)',
60
+ instructions: [
61
+ 'The completion script is already installed and up to date.',
62
+ 'Fish automatically loads completions - they should be available immediately.',
63
+ ],
64
+ };
65
+ }
66
+ // File exists but content is different - this is an update
67
+ isUpdate = true;
68
+ }
69
+ catch (error) {
70
+ // File doesn't exist or can't be read, proceed with installation
71
+ console.debug(`Unable to read existing completion file at ${targetPath}: ${error.message}`);
72
+ }
73
+ // Ensure the directory exists
74
+ const targetDir = path.dirname(targetPath);
75
+ await fs.mkdir(targetDir, { recursive: true });
76
+ // Backup existing file if updating
77
+ const backupPath = isUpdate ? await this.backupExistingFile(targetPath) : undefined;
78
+ // Write the completion script
79
+ await fs.writeFile(targetPath, completionScript, 'utf-8');
80
+ // Determine appropriate message
81
+ let message;
82
+ if (isUpdate) {
83
+ message = backupPath
84
+ ? 'Completion script updated successfully (previous version backed up)'
85
+ : 'Completion script updated successfully';
86
+ }
87
+ else {
88
+ message = 'Completion script installed successfully for Fish';
89
+ }
90
+ return {
91
+ success: true,
92
+ installedPath: targetPath,
93
+ backupPath,
94
+ message,
95
+ instructions: [
96
+ 'Fish automatically loads completions from ~/.config/fish/completions/',
97
+ 'Completions are available immediately - no shell restart needed.',
98
+ ],
99
+ };
100
+ }
101
+ catch (error) {
102
+ return {
103
+ success: false,
104
+ message: `Failed to install completion script: ${error instanceof Error ? error.message : String(error)}`,
105
+ };
106
+ }
107
+ }
108
+ /**
109
+ * Uninstall the completion script
110
+ *
111
+ * @param options - Optional uninstall options
112
+ * @param options.yes - Skip confirmation prompt (handled by command layer)
113
+ * @returns Uninstallation result
114
+ */
115
+ async uninstall(options) {
116
+ try {
117
+ const targetPath = this.getInstallationPath();
118
+ // Check if installed
119
+ try {
120
+ await fs.access(targetPath);
121
+ }
122
+ catch {
123
+ return {
124
+ success: false,
125
+ message: 'Completion script is not installed',
126
+ };
127
+ }
128
+ // Remove the completion script
129
+ await fs.unlink(targetPath);
130
+ return {
131
+ success: true,
132
+ message: 'Completion script uninstalled successfully',
133
+ };
134
+ }
135
+ catch (error) {
136
+ return {
137
+ success: false,
138
+ message: `Failed to uninstall completion script: ${error instanceof Error ? error.message : String(error)}`,
139
+ };
140
+ }
141
+ }
142
+ }
143
+ //# sourceMappingURL=fish-installer.js.map
@@ -0,0 +1,102 @@
1
+ import { InstallationResult } from '../factory.js';
2
+ /**
3
+ * Installer for PowerShell completion scripts.
4
+ * Works with both Windows PowerShell 5.1 and PowerShell Core 7+
5
+ */
6
+ export declare class PowerShellInstaller {
7
+ private readonly homeDir;
8
+ /**
9
+ * Markers for PowerShell profile configuration management
10
+ */
11
+ private readonly PROFILE_MARKERS;
12
+ constructor(homeDir?: string);
13
+ /**
14
+ * Detect the encoding of a file by inspecting its BOM (Byte Order Mark).
15
+ * Returns the Node.js BufferEncoding and the raw BOM bytes to preserve on write.
16
+ */
17
+ private detectEncoding;
18
+ /**
19
+ * Read a profile file, preserving its encoding metadata for round-trip writes.
20
+ * Throws if the file uses UTF-16 BE (unsupported by Node).
21
+ */
22
+ private readProfileFile;
23
+ /**
24
+ * Write a profile file, preserving the original BOM and encoding.
25
+ */
26
+ private writeProfileFile;
27
+ /**
28
+ * Get PowerShell profile path
29
+ * Prefers $PROFILE environment variable, falls back to platform defaults
30
+ *
31
+ * @returns Profile path
32
+ */
33
+ getProfilePath(): string;
34
+ /**
35
+ * Get all PowerShell profile paths to configure.
36
+ * On Windows, returns both PowerShell Core and Windows PowerShell 5.1 paths.
37
+ * On Unix, returns PowerShell Core path only.
38
+ */
39
+ private getAllProfilePaths;
40
+ /**
41
+ * Get the installation path for the completion script
42
+ *
43
+ * @returns Installation path
44
+ */
45
+ getInstallationPath(): string;
46
+ /**
47
+ * Backup an existing completion file if it exists
48
+ *
49
+ * @param targetPath - Path to the file to backup
50
+ * @returns Path to the backup file, or undefined if no backup was needed
51
+ */
52
+ backupExistingFile(targetPath: string): Promise<string | undefined>;
53
+ /**
54
+ * Generate PowerShell profile configuration content
55
+ *
56
+ * @param scriptPath - Path to the completion script
57
+ * @returns Configuration content
58
+ */
59
+ private generateProfileConfig;
60
+ /**
61
+ * Configure PowerShell profile to source the completion script
62
+ *
63
+ * @param scriptPath - Path to the completion script
64
+ * @returns true if configured successfully, false otherwise
65
+ */
66
+ configureProfile(scriptPath: string): Promise<boolean>;
67
+ /**
68
+ * Remove PowerShell profile configuration
69
+ * Used during uninstallation
70
+ *
71
+ * @returns true if removed successfully, false otherwise
72
+ */
73
+ removeProfileConfig(): Promise<boolean>;
74
+ /**
75
+ * Install the completion script
76
+ *
77
+ * @param completionScript - The completion script content to install
78
+ * @returns Installation result with status and instructions
79
+ */
80
+ install(completionScript: string): Promise<InstallationResult>;
81
+ /**
82
+ * Generate user instructions for enabling completions
83
+ *
84
+ * @param installedPath - Path where the script was installed
85
+ * @returns Array of instruction strings
86
+ */
87
+ private generateInstructions;
88
+ /**
89
+ * Uninstall the completion script
90
+ *
91
+ * @param options - Optional uninstall options
92
+ * @param options.yes - Skip confirmation prompt (handled by command layer)
93
+ * @returns Uninstallation result
94
+ */
95
+ uninstall(options?: {
96
+ yes?: boolean;
97
+ }): Promise<{
98
+ success: boolean;
99
+ message: string;
100
+ }>;
101
+ }
102
+ //# sourceMappingURL=powershell-installer.d.ts.map