@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,450 @@
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 Zsh completion scripts.
8
+ * Supports both Oh My Zsh and standard Zsh configurations.
9
+ */
10
+ export class ZshInstaller {
11
+ homeDir;
12
+ /**
13
+ * Markers for .zshrc configuration management
14
+ */
15
+ ZSHRC_MARKERS = {
16
+ start: '# OPENSPEC:START',
17
+ end: '# OPENSPEC:END',
18
+ };
19
+ constructor(homeDir = os.homedir()) {
20
+ this.homeDir = homeDir;
21
+ }
22
+ /**
23
+ * Check if Oh My Zsh is installed
24
+ *
25
+ * @returns true if Oh My Zsh is detected via $ZSH env var or directory exists
26
+ */
27
+ async isOhMyZshInstalled() {
28
+ // First check for $ZSH environment variable (standard OMZ setup)
29
+ if (process.env.ZSH) {
30
+ return true;
31
+ }
32
+ // Fall back to checking for ~/.oh-my-zsh directory
33
+ const ohMyZshPath = path.join(this.homeDir, '.oh-my-zsh');
34
+ try {
35
+ const stat = await fs.stat(ohMyZshPath);
36
+ return stat.isDirectory();
37
+ }
38
+ catch {
39
+ return false;
40
+ }
41
+ }
42
+ /**
43
+ * Get the appropriate installation path for the completion script
44
+ *
45
+ * @returns Object with installation path and whether it's Oh My Zsh
46
+ */
47
+ async getInstallationPath() {
48
+ const isOhMyZsh = await this.isOhMyZshInstalled();
49
+ if (isOhMyZsh) {
50
+ // Oh My Zsh custom completions directory
51
+ return {
52
+ path: path.join(this.homeDir, '.oh-my-zsh', 'custom', 'completions', '_openspec'),
53
+ isOhMyZsh: true,
54
+ };
55
+ }
56
+ else {
57
+ // Standard Zsh completions directory
58
+ return {
59
+ path: path.join(this.homeDir, '.zsh', 'completions', '_openspec'),
60
+ isOhMyZsh: false,
61
+ };
62
+ }
63
+ }
64
+ /**
65
+ * Backup an existing completion file if it exists
66
+ *
67
+ * @param targetPath - Path to the file to backup
68
+ * @returns Path to the backup file, or undefined if no backup was needed
69
+ */
70
+ async backupExistingFile(targetPath) {
71
+ try {
72
+ await fs.access(targetPath);
73
+ // File exists, create a backup
74
+ const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
75
+ const backupPath = `${targetPath}.backup-${timestamp}`;
76
+ await fs.copyFile(targetPath, backupPath);
77
+ return backupPath;
78
+ }
79
+ catch {
80
+ // File doesn't exist, no backup needed
81
+ return undefined;
82
+ }
83
+ }
84
+ /**
85
+ * Get the path to .zshrc file
86
+ *
87
+ * @returns Path to .zshrc
88
+ */
89
+ getZshrcPath() {
90
+ return path.join(this.homeDir, '.zshrc');
91
+ }
92
+ /**
93
+ * Generate .zshrc configuration content
94
+ *
95
+ * @param completionsDir - Directory containing completion scripts
96
+ * @returns Configuration content
97
+ */
98
+ generateZshrcConfig(completionsDir) {
99
+ return [
100
+ '# BR-OpenSpec shell completions configuration',
101
+ `fpath=("${completionsDir}" $fpath)`,
102
+ 'autoload -Uz compinit',
103
+ 'compinit',
104
+ ].join('\n');
105
+ }
106
+ /**
107
+ * Configure .zshrc to enable completions
108
+ * Only applies to standard Zsh (not Oh My Zsh)
109
+ *
110
+ * @param completionsDir - Directory containing completion scripts
111
+ * @returns true if configured successfully, false otherwise
112
+ */
113
+ async configureZshrc(completionsDir) {
114
+ // Check if auto-configuration is disabled
115
+ if (process.env.OPENSPEC_NO_AUTO_CONFIG === '1') {
116
+ return false;
117
+ }
118
+ try {
119
+ const zshrcPath = this.getZshrcPath();
120
+ const config = this.generateZshrcConfig(completionsDir);
121
+ // Check write permissions
122
+ const canWrite = await FileSystemUtils.canWriteFile(zshrcPath);
123
+ if (!canWrite) {
124
+ return false;
125
+ }
126
+ // Use marker-based update
127
+ await FileSystemUtils.updateFileWithMarkers(zshrcPath, config, this.ZSHRC_MARKERS.start, this.ZSHRC_MARKERS.end);
128
+ return true;
129
+ }
130
+ catch (error) {
131
+ // Fail gracefully - don't break installation
132
+ console.debug(`Unable to configure .zshrc for completions: ${error.message}`);
133
+ return false;
134
+ }
135
+ }
136
+ /**
137
+ * Check if .zshrc has OpenSpec configuration markers
138
+ *
139
+ * @returns true if .zshrc exists and has markers
140
+ */
141
+ async hasZshrcConfig() {
142
+ try {
143
+ const zshrcPath = this.getZshrcPath();
144
+ const content = await fs.readFile(zshrcPath, 'utf-8');
145
+ return content.includes(this.ZSHRC_MARKERS.start) && content.includes(this.ZSHRC_MARKERS.end);
146
+ }
147
+ catch {
148
+ return false;
149
+ }
150
+ }
151
+ /**
152
+ * Check if fpath configuration is needed for a given directory
153
+ * Used to verify if Oh My Zsh (or other) completions directory is already in fpath
154
+ *
155
+ * @param completionsDir - Directory to check for in fpath
156
+ * @returns true if configuration is needed, false if directory is already referenced
157
+ */
158
+ async needsFpathConfig(completionsDir) {
159
+ try {
160
+ const zshrcPath = this.getZshrcPath();
161
+ const content = await fs.readFile(zshrcPath, 'utf-8');
162
+ // Check if fpath already includes this directory
163
+ return !content.includes(completionsDir);
164
+ }
165
+ catch (error) {
166
+ // If we can't read .zshrc, assume config is needed
167
+ console.debug(`Unable to read .zshrc to check fpath config: ${error instanceof Error ? error.message : String(error)}`);
168
+ return true;
169
+ }
170
+ }
171
+ /**
172
+ * Remove .zshrc configuration
173
+ * Used during uninstallation
174
+ *
175
+ * @returns true if removed successfully, false otherwise
176
+ */
177
+ async removeZshrcConfig() {
178
+ try {
179
+ const zshrcPath = this.getZshrcPath();
180
+ // Check if file exists
181
+ try {
182
+ await fs.access(zshrcPath);
183
+ }
184
+ catch {
185
+ // File doesn't exist, nothing to remove
186
+ return true;
187
+ }
188
+ // Read file content
189
+ const content = await fs.readFile(zshrcPath, 'utf-8');
190
+ // Check if markers exist
191
+ if (!content.includes(this.ZSHRC_MARKERS.start) || !content.includes(this.ZSHRC_MARKERS.end)) {
192
+ // Markers don't exist, nothing to remove
193
+ return true;
194
+ }
195
+ // Remove content between markers (including markers)
196
+ const lines = content.split('\n');
197
+ const startIndex = lines.findIndex((line) => line.trim() === this.ZSHRC_MARKERS.start);
198
+ const endIndex = lines.findIndex((line) => line.trim() === this.ZSHRC_MARKERS.end);
199
+ if (startIndex === -1 || endIndex === -1 || endIndex < startIndex) {
200
+ // Invalid marker placement
201
+ return false;
202
+ }
203
+ // Remove lines between markers (inclusive)
204
+ lines.splice(startIndex, endIndex - startIndex + 1);
205
+ // Remove trailing empty lines at the start if the markers were at the top
206
+ while (lines.length > 0 && lines[0].trim() === '') {
207
+ lines.shift();
208
+ }
209
+ // Write back
210
+ await fs.writeFile(zshrcPath, lines.join('\n'), 'utf-8');
211
+ return true;
212
+ }
213
+ catch (error) {
214
+ // Fail gracefully
215
+ console.debug(`Unable to remove .zshrc configuration: ${error.message}`);
216
+ return false;
217
+ }
218
+ }
219
+ /**
220
+ * Install the completion script
221
+ *
222
+ * @param completionScript - The completion script content to install
223
+ * @returns Installation result with status and instructions
224
+ */
225
+ async install(completionScript) {
226
+ try {
227
+ const { path: targetPath, isOhMyZsh } = await this.getInstallationPath();
228
+ // Check if already installed with same content
229
+ let isUpdate = false;
230
+ try {
231
+ const existingContent = await fs.readFile(targetPath, 'utf-8');
232
+ if (existingContent === completionScript) {
233
+ // Already installed and up to date
234
+ return {
235
+ success: true,
236
+ installedPath: targetPath,
237
+ isOhMyZsh,
238
+ message: COMPLETION_MESSAGES.zshAlreadyInstalled,
239
+ instructions: [
240
+ COMPLETION_MESSAGES.zshAlreadyInstalledDetail,
241
+ COMPLETION_MESSAGES.zshAlreadyInstalledHint,
242
+ ],
243
+ };
244
+ }
245
+ // File exists but content is different - this is an update
246
+ isUpdate = true;
247
+ }
248
+ catch (error) {
249
+ // File doesn't exist or can't be read, proceed with installation
250
+ console.debug(`Unable to read existing completion file at ${targetPath}: ${error.message}`);
251
+ }
252
+ // Ensure the directory exists
253
+ const targetDir = path.dirname(targetPath);
254
+ await fs.mkdir(targetDir, { recursive: true });
255
+ // Backup existing file if updating
256
+ const backupPath = isUpdate ? await this.backupExistingFile(targetPath) : undefined;
257
+ // Write the completion script
258
+ await fs.writeFile(targetPath, completionScript, 'utf-8');
259
+ // Auto-configure .zshrc
260
+ let zshrcConfigured = false;
261
+ if (isOhMyZsh) {
262
+ // For Oh My Zsh, verify that custom/completions is in fpath
263
+ // If not, add it to .zshrc
264
+ const needsConfig = await this.needsFpathConfig(targetDir);
265
+ if (needsConfig) {
266
+ zshrcConfigured = await this.configureZshrc(targetDir);
267
+ }
268
+ }
269
+ else {
270
+ // Standard Zsh always needs .zshrc configuration
271
+ zshrcConfigured = await this.configureZshrc(targetDir);
272
+ }
273
+ // Generate instructions (only if .zshrc wasn't auto-configured)
274
+ let instructions = zshrcConfigured ? undefined : this.generateInstructions(isOhMyZsh, targetPath);
275
+ // Add fpath guidance for Oh My Zsh installations
276
+ if (isOhMyZsh) {
277
+ const fpathGuidance = this.generateOhMyZshFpathGuidance(targetDir);
278
+ if (fpathGuidance) {
279
+ instructions = instructions ? [...instructions, '', ...fpathGuidance] : fpathGuidance;
280
+ }
281
+ }
282
+ // Determine appropriate message based on update status
283
+ let message;
284
+ if (isUpdate) {
285
+ message = backupPath
286
+ ? COMPLETION_MESSAGES.zshUpdatedWithBackup
287
+ : COMPLETION_MESSAGES.zshUpdated;
288
+ }
289
+ else {
290
+ message = isOhMyZsh
291
+ ? COMPLETION_MESSAGES.zshInstalledOhMyZsh
292
+ : zshrcConfigured
293
+ ? COMPLETION_MESSAGES.zshInstalledWithZshrc
294
+ : COMPLETION_MESSAGES.zshInstalled;
295
+ }
296
+ return {
297
+ success: true,
298
+ installedPath: targetPath,
299
+ backupPath,
300
+ isOhMyZsh,
301
+ zshrcConfigured,
302
+ message,
303
+ instructions,
304
+ };
305
+ }
306
+ catch (error) {
307
+ return {
308
+ success: false,
309
+ isOhMyZsh: false,
310
+ message: COMPLETION_MESSAGES.zshFailedToInstall(error instanceof Error ? error.message : String(error)),
311
+ };
312
+ }
313
+ }
314
+ /**
315
+ * Generate Oh My Zsh fpath verification guidance
316
+ *
317
+ * @param completionsDir - Custom completions directory path
318
+ * @returns Array of guidance strings, or undefined if not needed
319
+ */
320
+ generateOhMyZshFpathGuidance(completionsDir) {
321
+ return [
322
+ COMPLETION_MESSAGES.zshOhMyZshFpathNote,
323
+ COMPLETION_MESSAGES.zshOhMyZshFpathVerify(completionsDir),
324
+ ' echo $fpath | grep "custom/completions"',
325
+ '',
326
+ COMPLETION_MESSAGES.zshOhMyZshFpathRestart,
327
+ ];
328
+ }
329
+ /**
330
+ * Generate user instructions for enabling completions
331
+ *
332
+ * @param isOhMyZsh - Whether Oh My Zsh is being used
333
+ * @param installedPath - Path where the script was installed
334
+ * @returns Array of instruction strings
335
+ */
336
+ generateInstructions(isOhMyZsh, installedPath) {
337
+ if (isOhMyZsh) {
338
+ return [
339
+ COMPLETION_MESSAGES.zshOhMyZshInstalledDir,
340
+ COMPLETION_MESSAGES.restartShell('exec zsh'),
341
+ COMPLETION_MESSAGES.zshOhMyZshAutoActivate,
342
+ ];
343
+ }
344
+ else {
345
+ const completionsDir = path.dirname(installedPath);
346
+ const zshrcPath = path.join(this.homeDir, '.zshrc');
347
+ return [
348
+ COMPLETION_MESSAGES.zshInstalledDir,
349
+ '',
350
+ COMPLETION_MESSAGES.zshAddToZshrc,
351
+ '',
352
+ ` ${COMPLETION_MESSAGES.zshFpathComment}`,
353
+ ` fpath=(${completionsDir} $fpath)`,
354
+ '',
355
+ ` ${COMPLETION_MESSAGES.zshCompinitComment}`,
356
+ ' autoload -Uz compinit',
357
+ ' compinit',
358
+ '',
359
+ COMPLETION_MESSAGES.zshThenRestartShell('exec zsh'),
360
+ '',
361
+ COMPLETION_MESSAGES.zshCheckExistingLines(zshrcPath),
362
+ ];
363
+ }
364
+ }
365
+ /**
366
+ * Uninstall the completion script
367
+ *
368
+ * @returns true if uninstalled successfully, false otherwise
369
+ */
370
+ async uninstall() {
371
+ try {
372
+ const { path: targetPath, isOhMyZsh } = await this.getInstallationPath();
373
+ // Try to remove completion script
374
+ let scriptRemoved = false;
375
+ try {
376
+ await fs.access(targetPath);
377
+ await fs.unlink(targetPath);
378
+ scriptRemoved = true;
379
+ }
380
+ catch {
381
+ // Script not installed
382
+ }
383
+ // Try to remove .zshrc configuration (only for standard Zsh)
384
+ let zshrcWasPresent = false;
385
+ let zshrcCleaned = false;
386
+ if (!isOhMyZsh) {
387
+ zshrcWasPresent = await this.hasZshrcConfig();
388
+ if (zshrcWasPresent) {
389
+ zshrcCleaned = await this.removeZshrcConfig();
390
+ }
391
+ }
392
+ if (!scriptRemoved && !zshrcWasPresent) {
393
+ return {
394
+ success: false,
395
+ message: 'Completion script is not installed',
396
+ };
397
+ }
398
+ const messages = [];
399
+ if (scriptRemoved) {
400
+ messages.push(COMPLETION_MESSAGES.zshScriptRemoved(targetPath));
401
+ }
402
+ if (zshrcCleaned && !isOhMyZsh) {
403
+ messages.push(COMPLETION_MESSAGES.zshConfigRemoved);
404
+ }
405
+ return {
406
+ success: true,
407
+ message: messages.join('. '),
408
+ };
409
+ }
410
+ catch (error) {
411
+ return {
412
+ success: false,
413
+ message: COMPLETION_MESSAGES.failedToUninstall(error instanceof Error ? error.message : String(error)),
414
+ };
415
+ }
416
+ }
417
+ /**
418
+ * Check if completion script is currently installed
419
+ *
420
+ * @returns true if the completion script exists
421
+ */
422
+ async isInstalled() {
423
+ try {
424
+ const { path: targetPath } = await this.getInstallationPath();
425
+ await fs.access(targetPath);
426
+ return true;
427
+ }
428
+ catch {
429
+ return false;
430
+ }
431
+ }
432
+ /**
433
+ * Get information about the current installation
434
+ *
435
+ * @returns Installation status information
436
+ */
437
+ async getInstallationInfo() {
438
+ const installed = await this.isInstalled();
439
+ if (!installed) {
440
+ return { installed: false };
441
+ }
442
+ const { path: targetPath, isOhMyZsh } = await this.getInstallationPath();
443
+ return {
444
+ installed: true,
445
+ path: targetPath,
446
+ isOhMyZsh,
447
+ };
448
+ }
449
+ }
450
+ //# sourceMappingURL=zsh-installer.js.map
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Static template strings for Bash completion scripts.
3
+ * These are Bash-specific helper functions that never change.
4
+ */
5
+ export declare const BASH_DYNAMIC_HELPERS = "# Dynamic completion helpers\n\n_openspec_complete_changes() {\n local changes\n changes=$(openspec __complete changes 2>/dev/null | cut -f1)\n COMPREPLY=($(compgen -W \"$changes\" -- \"$cur\"))\n}\n\n_openspec_complete_specs() {\n local specs\n specs=$(openspec __complete specs 2>/dev/null | cut -f1)\n COMPREPLY=($(compgen -W \"$specs\" -- \"$cur\"))\n}\n\n_openspec_complete_items() {\n local items\n items=$(openspec __complete changes 2>/dev/null | cut -f1; openspec __complete specs 2>/dev/null | cut -f1)\n COMPREPLY=($(compgen -W \"$items\" -- \"$cur\"))\n}";
6
+ //# sourceMappingURL=bash-templates.d.ts.map
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Static template strings for Bash completion scripts.
3
+ * These are Bash-specific helper functions that never change.
4
+ */
5
+ export const BASH_DYNAMIC_HELPERS = `# Dynamic completion helpers
6
+
7
+ _openspec_complete_changes() {
8
+ local changes
9
+ changes=$(openspec __complete changes 2>/dev/null | cut -f1)
10
+ COMPREPLY=($(compgen -W "$changes" -- "$cur"))
11
+ }
12
+
13
+ _openspec_complete_specs() {
14
+ local specs
15
+ specs=$(openspec __complete specs 2>/dev/null | cut -f1)
16
+ COMPREPLY=($(compgen -W "$specs" -- "$cur"))
17
+ }
18
+
19
+ _openspec_complete_items() {
20
+ local items
21
+ items=$(openspec __complete changes 2>/dev/null | cut -f1; openspec __complete specs 2>/dev/null | cut -f1)
22
+ COMPREPLY=($(compgen -W "$items" -- "$cur"))
23
+ }`;
24
+ //# sourceMappingURL=bash-templates.js.map
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Static template strings for Fish completion scripts.
3
+ * These are Fish-specific helper functions that never change.
4
+ */
5
+ export declare const FISH_STATIC_HELPERS = "# Helper function to check if a subcommand is present\nfunction __fish_openspec_using_subcommand\n set -l cmd (commandline -opc)\n set -e cmd[1]\n for i in $argv\n if contains -- $i $cmd\n return 0\n end\n end\n return 1\nend\n\nfunction __fish_openspec_no_subcommand\n set -l cmd (commandline -opc)\n test (count $cmd) -eq 1\nend";
6
+ export declare const FISH_DYNAMIC_HELPERS = "# Dynamic completion helpers\n\nfunction __fish_openspec_changes\n openspec __complete changes 2>/dev/null | while read -l id desc\n printf '%s\\t%s\\n' \"$id\" \"$desc\"\n end\nend\n\nfunction __fish_openspec_specs\n openspec __complete specs 2>/dev/null | while read -l id desc\n printf '%s\\t%s\\n' \"$id\" \"$desc\"\n end\nend\n\nfunction __fish_openspec_items\n __fish_openspec_changes\n __fish_openspec_specs\nend";
7
+ //# sourceMappingURL=fish-templates.d.ts.map
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Static template strings for Fish completion scripts.
3
+ * These are Fish-specific helper functions that never change.
4
+ */
5
+ export const FISH_STATIC_HELPERS = `# Helper function to check if a subcommand is present
6
+ function __fish_openspec_using_subcommand
7
+ set -l cmd (commandline -opc)
8
+ set -e cmd[1]
9
+ for i in $argv
10
+ if contains -- $i $cmd
11
+ return 0
12
+ end
13
+ end
14
+ return 1
15
+ end
16
+
17
+ function __fish_openspec_no_subcommand
18
+ set -l cmd (commandline -opc)
19
+ test (count $cmd) -eq 1
20
+ end`;
21
+ export const FISH_DYNAMIC_HELPERS = `# Dynamic completion helpers
22
+
23
+ function __fish_openspec_changes
24
+ openspec __complete changes 2>/dev/null | while read -l id desc
25
+ printf '%s\\t%s\\n' "$id" "$desc"
26
+ end
27
+ end
28
+
29
+ function __fish_openspec_specs
30
+ openspec __complete specs 2>/dev/null | while read -l id desc
31
+ printf '%s\\t%s\\n' "$id" "$desc"
32
+ end
33
+ end
34
+
35
+ function __fish_openspec_items
36
+ __fish_openspec_changes
37
+ __fish_openspec_specs
38
+ end`;
39
+ //# sourceMappingURL=fish-templates.js.map
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Static template strings for PowerShell completion scripts.
3
+ * These are PowerShell-specific helper functions that never change.
4
+ */
5
+ export declare const POWERSHELL_DYNAMIC_HELPERS = "# Dynamic completion helpers\n\nfunction Get-BROpenSpecChanges {\n $output = openspec __complete changes 2>$null\n if ($output) {\n $output | ForEach-Object {\n ($_ -split \"\\t\")[0]\n }\n }\n}\n\nfunction Get-BROpenSpecSpecs {\n $output = openspec __complete specs 2>$null\n if ($output) {\n $output | ForEach-Object {\n ($_ -split \"\\t\")[0]\n }\n }\n}\n";
6
+ //# sourceMappingURL=powershell-templates.d.ts.map
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Static template strings for PowerShell completion scripts.
3
+ * These are PowerShell-specific helper functions that never change.
4
+ */
5
+ export const POWERSHELL_DYNAMIC_HELPERS = `# Dynamic completion helpers
6
+
7
+ function Get-BROpenSpecChanges {
8
+ $output = openspec __complete changes 2>$null
9
+ if ($output) {
10
+ $output | ForEach-Object {
11
+ ($_ -split "\\t")[0]
12
+ }
13
+ }
14
+ }
15
+
16
+ function Get-BROpenSpecSpecs {
17
+ $output = openspec __complete specs 2>$null
18
+ if ($output) {
19
+ $output | ForEach-Object {
20
+ ($_ -split "\\t")[0]
21
+ }
22
+ }
23
+ }
24
+ `;
25
+ //# sourceMappingURL=powershell-templates.js.map
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Static template strings for Zsh completion scripts.
3
+ * These are Zsh-specific helper functions that never change.
4
+ */
5
+ export declare const ZSH_DYNAMIC_HELPERS = "# Dynamic completion helpers\n\n# Use openspec __complete to get available changes\n_openspec_complete_changes() {\n local -a changes\n while IFS=$'\\t' read -r id desc; do\n changes+=(\"$id:$desc\")\n done < <(openspec __complete changes 2>/dev/null)\n _describe \"change\" changes\n}\n\n# Use openspec __complete to get available specs\n_openspec_complete_specs() {\n local -a specs\n while IFS=$'\\t' read -r id desc; do\n specs+=(\"$id:$desc\")\n done < <(openspec __complete specs 2>/dev/null)\n _describe \"spec\" specs\n}\n\n# Get both changes and specs\n_openspec_complete_items() {\n local -a items\n while IFS=$'\\t' read -r id desc; do\n items+=(\"$id:$desc\")\n done < <(openspec __complete changes 2>/dev/null)\n while IFS=$'\\t' read -r id desc; do\n items+=(\"$id:$desc\")\n done < <(openspec __complete specs 2>/dev/null)\n _describe \"item\" items\n}";
6
+ //# sourceMappingURL=zsh-templates.d.ts.map
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Static template strings for Zsh completion scripts.
3
+ * These are Zsh-specific helper functions that never change.
4
+ */
5
+ export const ZSH_DYNAMIC_HELPERS = `# Dynamic completion helpers
6
+
7
+ # Use openspec __complete to get available changes
8
+ _openspec_complete_changes() {
9
+ local -a changes
10
+ while IFS=$'\\t' read -r id desc; do
11
+ changes+=("$id:$desc")
12
+ done < <(openspec __complete changes 2>/dev/null)
13
+ _describe "change" changes
14
+ }
15
+
16
+ # Use openspec __complete to get available specs
17
+ _openspec_complete_specs() {
18
+ local -a specs
19
+ while IFS=$'\\t' read -r id desc; do
20
+ specs+=("$id:$desc")
21
+ done < <(openspec __complete specs 2>/dev/null)
22
+ _describe "spec" specs
23
+ }
24
+
25
+ # Get both changes and specs
26
+ _openspec_complete_items() {
27
+ local -a items
28
+ while IFS=$'\\t' read -r id desc; do
29
+ items+=("$id:$desc")
30
+ done < <(openspec __complete changes 2>/dev/null)
31
+ while IFS=$'\\t' read -r id desc; do
32
+ items+=("$id:$desc")
33
+ done < <(openspec __complete specs 2>/dev/null)
34
+ _describe "item" items
35
+ }`;
36
+ //# sourceMappingURL=zsh-templates.js.map
@@ -0,0 +1,79 @@
1
+ import { SupportedShell } from '../../utils/shell-detection.js';
2
+ /**
3
+ * Definition of a command-line flag/option
4
+ */
5
+ export interface FlagDefinition {
6
+ /**
7
+ * Flag name without dashes (e.g., "json", "strict", "no-interactive")
8
+ */
9
+ name: string;
10
+ /**
11
+ * Short flag name without dash (e.g., "y" for "-y")
12
+ */
13
+ short?: string;
14
+ /**
15
+ * Human-readable description of what the flag does
16
+ */
17
+ description: string;
18
+ /**
19
+ * Whether the flag takes an argument value
20
+ */
21
+ takesValue?: boolean;
22
+ /**
23
+ * Possible values for the flag (for completion suggestions)
24
+ */
25
+ values?: string[];
26
+ }
27
+ /**
28
+ * Definition of a CLI command
29
+ */
30
+ export interface CommandDefinition {
31
+ /**
32
+ * Command name (e.g., "init", "validate", "show")
33
+ */
34
+ name: string;
35
+ /**
36
+ * Human-readable description of the command
37
+ */
38
+ description: string;
39
+ /**
40
+ * Flags/options supported by this command
41
+ */
42
+ flags: FlagDefinition[];
43
+ /**
44
+ * Subcommands (e.g., "change show", "spec validate")
45
+ */
46
+ subcommands?: CommandDefinition[];
47
+ /**
48
+ * Whether this command accepts a positional argument (e.g., item name, path)
49
+ */
50
+ acceptsPositional?: boolean;
51
+ /**
52
+ * Type of positional argument for dynamic completion
53
+ * - 'change-id': Complete with active change IDs
54
+ * - 'spec-id': Complete with spec IDs
55
+ * - 'change-or-spec-id': Complete with both changes and specs
56
+ * - 'path': Complete with file paths
57
+ * - 'shell': Complete with supported shell names
58
+ * - 'schema-name': Complete with available schema names
59
+ * - undefined: No specific completion
60
+ */
61
+ positionalType?: 'change-id' | 'spec-id' | 'change-or-spec-id' | 'path' | 'shell' | 'schema-name';
62
+ }
63
+ /**
64
+ * Interface for shell-specific completion script generators
65
+ */
66
+ export interface CompletionGenerator {
67
+ /**
68
+ * The shell type this generator targets
69
+ */
70
+ readonly shell: SupportedShell;
71
+ /**
72
+ * Generate the completion script content
73
+ *
74
+ * @param commands - Command definitions to generate completions for
75
+ * @returns The shell-specific completion script as a string
76
+ */
77
+ generate(commands: CommandDefinition[]): string;
78
+ }
79
+ //# sourceMappingURL=types.d.ts.map