@codewalla_india/openspec 1.0.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 (356) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +225 -0
  3. package/bin/openspec.js +5 -0
  4. package/dist/cli/index.d.ts +10 -0
  5. package/dist/cli/index.js +548 -0
  6. package/dist/commands/change.d.ts +39 -0
  7. package/dist/commands/change.js +279 -0
  8. package/dist/commands/completion.d.ts +72 -0
  9. package/dist/commands/completion.js +264 -0
  10. package/dist/commands/config.d.ts +36 -0
  11. package/dist/commands/config.js +552 -0
  12. package/dist/commands/context.d.ts +3 -0
  13. package/dist/commands/context.js +155 -0
  14. package/dist/commands/doctor.d.ts +8 -0
  15. package/dist/commands/doctor.js +163 -0
  16. package/dist/commands/feedback.d.ts +9 -0
  17. package/dist/commands/feedback.js +183 -0
  18. package/dist/commands/schema.d.ts +6 -0
  19. package/dist/commands/schema.js +869 -0
  20. package/dist/commands/shared-gather.d.ts +14 -0
  21. package/dist/commands/shared-gather.js +31 -0
  22. package/dist/commands/shared-output.d.ts +18 -0
  23. package/dist/commands/shared-output.js +61 -0
  24. package/dist/commands/show.d.ts +19 -0
  25. package/dist/commands/show.js +177 -0
  26. package/dist/commands/spec.d.ts +19 -0
  27. package/dist/commands/spec.js +236 -0
  28. package/dist/commands/store.d.ts +3 -0
  29. package/dist/commands/store.js +547 -0
  30. package/dist/commands/validate.d.ts +26 -0
  31. package/dist/commands/validate.js +330 -0
  32. package/dist/commands/workflow/index.d.ts +17 -0
  33. package/dist/commands/workflow/index.js +12 -0
  34. package/dist/commands/workflow/instructions.d.ts +45 -0
  35. package/dist/commands/workflow/instructions.js +500 -0
  36. package/dist/commands/workflow/new-change.d.ts +20 -0
  37. package/dist/commands/workflow/new-change.js +106 -0
  38. package/dist/commands/workflow/schemas.d.ts +10 -0
  39. package/dist/commands/workflow/schemas.js +34 -0
  40. package/dist/commands/workflow/shared.d.ts +84 -0
  41. package/dist/commands/workflow/shared.js +133 -0
  42. package/dist/commands/workflow/status.d.ts +16 -0
  43. package/dist/commands/workflow/status.js +92 -0
  44. package/dist/commands/workflow/templates.d.ts +16 -0
  45. package/dist/commands/workflow/templates.js +69 -0
  46. package/dist/commands/workset-input.d.ts +19 -0
  47. package/dist/commands/workset-input.js +112 -0
  48. package/dist/commands/workset-prompts.d.ts +12 -0
  49. package/dist/commands/workset-prompts.js +143 -0
  50. package/dist/commands/workset.d.ts +25 -0
  51. package/dist/commands/workset.js +446 -0
  52. package/dist/core/archive.d.ts +22 -0
  53. package/dist/core/archive.js +471 -0
  54. package/dist/core/artifact-graph/graph.d.ts +56 -0
  55. package/dist/core/artifact-graph/graph.js +141 -0
  56. package/dist/core/artifact-graph/index.d.ts +9 -0
  57. package/dist/core/artifact-graph/index.js +14 -0
  58. package/dist/core/artifact-graph/instruction-loader.d.ts +188 -0
  59. package/dist/core/artifact-graph/instruction-loader.js +233 -0
  60. package/dist/core/artifact-graph/outputs.d.ts +14 -0
  61. package/dist/core/artifact-graph/outputs.js +39 -0
  62. package/dist/core/artifact-graph/resolver.d.ts +81 -0
  63. package/dist/core/artifact-graph/resolver.js +257 -0
  64. package/dist/core/artifact-graph/schema.d.ts +13 -0
  65. package/dist/core/artifact-graph/schema.js +108 -0
  66. package/dist/core/artifact-graph/state.d.ts +12 -0
  67. package/dist/core/artifact-graph/state.js +31 -0
  68. package/dist/core/artifact-graph/types.d.ts +40 -0
  69. package/dist/core/artifact-graph/types.js +29 -0
  70. package/dist/core/available-tools.d.ts +17 -0
  71. package/dist/core/available-tools.js +43 -0
  72. package/dist/core/change-metadata/index.d.ts +2 -0
  73. package/dist/core/change-metadata/index.js +2 -0
  74. package/dist/core/change-metadata/schema.d.ts +19 -0
  75. package/dist/core/change-metadata/schema.js +30 -0
  76. package/dist/core/change-status-policy.d.ts +37 -0
  77. package/dist/core/change-status-policy.js +35 -0
  78. package/dist/core/command-generation/adapters/amazon-q.d.ts +13 -0
  79. package/dist/core/command-generation/adapters/amazon-q.js +26 -0
  80. package/dist/core/command-generation/adapters/antigravity.d.ts +13 -0
  81. package/dist/core/command-generation/adapters/antigravity.js +26 -0
  82. package/dist/core/command-generation/adapters/auggie.d.ts +13 -0
  83. package/dist/core/command-generation/adapters/auggie.js +27 -0
  84. package/dist/core/command-generation/adapters/bob.d.ts +14 -0
  85. package/dist/core/command-generation/adapters/bob.js +32 -0
  86. package/dist/core/command-generation/adapters/claude.d.ts +13 -0
  87. package/dist/core/command-generation/adapters/claude.js +37 -0
  88. package/dist/core/command-generation/adapters/cline.d.ts +14 -0
  89. package/dist/core/command-generation/adapters/cline.js +27 -0
  90. package/dist/core/command-generation/adapters/codebuddy.d.ts +13 -0
  91. package/dist/core/command-generation/adapters/codebuddy.js +28 -0
  92. package/dist/core/command-generation/adapters/codex.d.ts +16 -0
  93. package/dist/core/command-generation/adapters/codex.js +39 -0
  94. package/dist/core/command-generation/adapters/continue.d.ts +13 -0
  95. package/dist/core/command-generation/adapters/continue.js +28 -0
  96. package/dist/core/command-generation/adapters/costrict.d.ts +13 -0
  97. package/dist/core/command-generation/adapters/costrict.js +27 -0
  98. package/dist/core/command-generation/adapters/crush.d.ts +13 -0
  99. package/dist/core/command-generation/adapters/crush.js +30 -0
  100. package/dist/core/command-generation/adapters/cursor.d.ts +14 -0
  101. package/dist/core/command-generation/adapters/cursor.js +31 -0
  102. package/dist/core/command-generation/adapters/factory.d.ts +13 -0
  103. package/dist/core/command-generation/adapters/factory.js +27 -0
  104. package/dist/core/command-generation/adapters/gemini.d.ts +13 -0
  105. package/dist/core/command-generation/adapters/gemini.js +26 -0
  106. package/dist/core/command-generation/adapters/github-copilot.d.ts +13 -0
  107. package/dist/core/command-generation/adapters/github-copilot.js +26 -0
  108. package/dist/core/command-generation/adapters/iflow.d.ts +13 -0
  109. package/dist/core/command-generation/adapters/iflow.js +29 -0
  110. package/dist/core/command-generation/adapters/index.d.ts +32 -0
  111. package/dist/core/command-generation/adapters/index.js +32 -0
  112. package/dist/core/command-generation/adapters/junie.d.ts +13 -0
  113. package/dist/core/command-generation/adapters/junie.js +26 -0
  114. package/dist/core/command-generation/adapters/kilocode.d.ts +14 -0
  115. package/dist/core/command-generation/adapters/kilocode.js +23 -0
  116. package/dist/core/command-generation/adapters/kiro.d.ts +13 -0
  117. package/dist/core/command-generation/adapters/kiro.js +26 -0
  118. package/dist/core/command-generation/adapters/lingma.d.ts +13 -0
  119. package/dist/core/command-generation/adapters/lingma.js +30 -0
  120. package/dist/core/command-generation/adapters/opencode.d.ts +13 -0
  121. package/dist/core/command-generation/adapters/opencode.js +29 -0
  122. package/dist/core/command-generation/adapters/pi.d.ts +18 -0
  123. package/dist/core/command-generation/adapters/pi.js +42 -0
  124. package/dist/core/command-generation/adapters/qoder.d.ts +13 -0
  125. package/dist/core/command-generation/adapters/qoder.js +30 -0
  126. package/dist/core/command-generation/adapters/qwen.d.ts +13 -0
  127. package/dist/core/command-generation/adapters/qwen.js +26 -0
  128. package/dist/core/command-generation/adapters/roocode.d.ts +14 -0
  129. package/dist/core/command-generation/adapters/roocode.js +27 -0
  130. package/dist/core/command-generation/adapters/windsurf.d.ts +14 -0
  131. package/dist/core/command-generation/adapters/windsurf.js +38 -0
  132. package/dist/core/command-generation/generator.d.ts +21 -0
  133. package/dist/core/command-generation/generator.js +27 -0
  134. package/dist/core/command-generation/index.d.ts +22 -0
  135. package/dist/core/command-generation/index.js +24 -0
  136. package/dist/core/command-generation/registry.d.ts +36 -0
  137. package/dist/core/command-generation/registry.js +98 -0
  138. package/dist/core/command-generation/types.d.ts +56 -0
  139. package/dist/core/command-generation/types.js +8 -0
  140. package/dist/core/command-generation/yaml.d.ts +22 -0
  141. package/dist/core/command-generation/yaml.js +38 -0
  142. package/dist/core/completions/command-registry.d.ts +3 -0
  143. package/dist/core/completions/command-registry.js +778 -0
  144. package/dist/core/completions/completion-provider.d.ts +71 -0
  145. package/dist/core/completions/completion-provider.js +129 -0
  146. package/dist/core/completions/factory.d.ts +64 -0
  147. package/dist/core/completions/factory.js +75 -0
  148. package/dist/core/completions/generators/bash-generator.d.ts +35 -0
  149. package/dist/core/completions/generators/bash-generator.js +230 -0
  150. package/dist/core/completions/generators/fish-generator.d.ts +32 -0
  151. package/dist/core/completions/generators/fish-generator.js +160 -0
  152. package/dist/core/completions/generators/powershell-generator.d.ts +36 -0
  153. package/dist/core/completions/generators/powershell-generator.js +266 -0
  154. package/dist/core/completions/generators/zsh-generator.d.ts +47 -0
  155. package/dist/core/completions/generators/zsh-generator.js +276 -0
  156. package/dist/core/completions/installers/bash-installer.d.ts +87 -0
  157. package/dist/core/completions/installers/bash-installer.js +321 -0
  158. package/dist/core/completions/installers/fish-installer.d.ts +43 -0
  159. package/dist/core/completions/installers/fish-installer.js +151 -0
  160. package/dist/core/completions/installers/powershell-installer.d.ts +102 -0
  161. package/dist/core/completions/installers/powershell-installer.js +415 -0
  162. package/dist/core/completions/installers/zsh-installer.d.ts +117 -0
  163. package/dist/core/completions/installers/zsh-installer.js +424 -0
  164. package/dist/core/completions/shared-flags.d.ts +13 -0
  165. package/dist/core/completions/shared-flags.js +33 -0
  166. package/dist/core/completions/templates/bash-templates.d.ts +6 -0
  167. package/dist/core/completions/templates/bash-templates.js +30 -0
  168. package/dist/core/completions/templates/fish-templates.d.ts +7 -0
  169. package/dist/core/completions/templates/fish-templates.js +45 -0
  170. package/dist/core/completions/templates/powershell-templates.d.ts +6 -0
  171. package/dist/core/completions/templates/powershell-templates.js +34 -0
  172. package/dist/core/completions/templates/zsh-templates.d.ts +6 -0
  173. package/dist/core/completions/templates/zsh-templates.js +45 -0
  174. package/dist/core/completions/types.d.ts +101 -0
  175. package/dist/core/completions/types.js +2 -0
  176. package/dist/core/comprehension/config.d.ts +20 -0
  177. package/dist/core/comprehension/config.js +23 -0
  178. package/dist/core/comprehension/fingerprint.d.ts +5 -0
  179. package/dist/core/comprehension/fingerprint.js +25 -0
  180. package/dist/core/comprehension/index.d.ts +49 -0
  181. package/dist/core/comprehension/index.js +78 -0
  182. package/dist/core/comprehension/pass-record.d.ts +29 -0
  183. package/dist/core/comprehension/pass-record.js +64 -0
  184. package/dist/core/comprehension/stats.d.ts +18 -0
  185. package/dist/core/comprehension/stats.js +41 -0
  186. package/dist/core/config-prompts.d.ts +9 -0
  187. package/dist/core/config-prompts.js +34 -0
  188. package/dist/core/config-schema.d.ts +87 -0
  189. package/dist/core/config-schema.js +239 -0
  190. package/dist/core/config.d.ts +18 -0
  191. package/dist/core/config.js +39 -0
  192. package/dist/core/converters/json-converter.d.ts +6 -0
  193. package/dist/core/converters/json-converter.js +51 -0
  194. package/dist/core/file-state.d.ts +36 -0
  195. package/dist/core/file-state.js +112 -0
  196. package/dist/core/global-config.d.ts +51 -0
  197. package/dist/core/global-config.js +124 -0
  198. package/dist/core/id.d.ts +17 -0
  199. package/dist/core/id.js +30 -0
  200. package/dist/core/index.d.ts +6 -0
  201. package/dist/core/index.js +7 -0
  202. package/dist/core/init.d.ts +37 -0
  203. package/dist/core/init.js +613 -0
  204. package/dist/core/legacy-cleanup.d.ts +162 -0
  205. package/dist/core/legacy-cleanup.js +514 -0
  206. package/dist/core/list.d.ts +11 -0
  207. package/dist/core/list.js +185 -0
  208. package/dist/core/migration.d.ts +23 -0
  209. package/dist/core/migration.js +108 -0
  210. package/dist/core/openers.d.ts +77 -0
  211. package/dist/core/openers.js +251 -0
  212. package/dist/core/openspec-root.d.ts +45 -0
  213. package/dist/core/openspec-root.js +192 -0
  214. package/dist/core/parsers/change-parser.d.ts +13 -0
  215. package/dist/core/parsers/change-parser.js +197 -0
  216. package/dist/core/parsers/markdown-parser.d.ts +26 -0
  217. package/dist/core/parsers/markdown-parser.js +227 -0
  218. package/dist/core/parsers/requirement-blocks.d.ts +37 -0
  219. package/dist/core/parsers/requirement-blocks.js +201 -0
  220. package/dist/core/parsers/spec-structure.d.ts +9 -0
  221. package/dist/core/parsers/spec-structure.js +88 -0
  222. package/dist/core/planning-home.d.ts +16 -0
  223. package/dist/core/planning-home.js +67 -0
  224. package/dist/core/profile-sync-drift.d.ts +38 -0
  225. package/dist/core/profile-sync-drift.js +200 -0
  226. package/dist/core/profiles.d.ts +26 -0
  227. package/dist/core/profiles.js +40 -0
  228. package/dist/core/project-config.d.ts +120 -0
  229. package/dist/core/project-config.js +406 -0
  230. package/dist/core/references.d.ts +63 -0
  231. package/dist/core/references.js +310 -0
  232. package/dist/core/relationship-health.d.ts +65 -0
  233. package/dist/core/relationship-health.js +64 -0
  234. package/dist/core/root-selection.d.ts +122 -0
  235. package/dist/core/root-selection.js +337 -0
  236. package/dist/core/schemas/base.schema.d.ts +13 -0
  237. package/dist/core/schemas/base.schema.js +13 -0
  238. package/dist/core/schemas/change.schema.d.ts +73 -0
  239. package/dist/core/schemas/change.schema.js +31 -0
  240. package/dist/core/schemas/index.d.ts +4 -0
  241. package/dist/core/schemas/index.js +4 -0
  242. package/dist/core/schemas/spec.schema.d.ts +18 -0
  243. package/dist/core/schemas/spec.schema.js +15 -0
  244. package/dist/core/shared/index.d.ts +8 -0
  245. package/dist/core/shared/index.js +8 -0
  246. package/dist/core/shared/skill-generation.d.ts +49 -0
  247. package/dist/core/shared/skill-generation.js +96 -0
  248. package/dist/core/shared/tool-detection.d.ts +71 -0
  249. package/dist/core/shared/tool-detection.js +158 -0
  250. package/dist/core/specs-apply.d.ts +78 -0
  251. package/dist/core/specs-apply.js +394 -0
  252. package/dist/core/store/errors.d.ts +20 -0
  253. package/dist/core/store/errors.js +22 -0
  254. package/dist/core/store/foundation.d.ts +56 -0
  255. package/dist/core/store/foundation.js +251 -0
  256. package/dist/core/store/git.d.ts +23 -0
  257. package/dist/core/store/git.js +137 -0
  258. package/dist/core/store/index.d.ts +5 -0
  259. package/dist/core/store/index.js +5 -0
  260. package/dist/core/store/operations.d.ts +114 -0
  261. package/dist/core/store/operations.js +783 -0
  262. package/dist/core/store/registry.d.ts +58 -0
  263. package/dist/core/store/registry.js +275 -0
  264. package/dist/core/styles/palette.d.ts +7 -0
  265. package/dist/core/styles/palette.js +8 -0
  266. package/dist/core/templates/index.d.ts +8 -0
  267. package/dist/core/templates/index.js +9 -0
  268. package/dist/core/templates/skill-templates.d.ts +19 -0
  269. package/dist/core/templates/skill-templates.js +18 -0
  270. package/dist/core/templates/types.d.ts +19 -0
  271. package/dist/core/templates/types.js +5 -0
  272. package/dist/core/templates/workflows/apply-change.d.ts +10 -0
  273. package/dist/core/templates/workflows/apply-change.js +337 -0
  274. package/dist/core/templates/workflows/archive-change.d.ts +10 -0
  275. package/dist/core/templates/workflows/archive-change.js +278 -0
  276. package/dist/core/templates/workflows/bulk-archive-change.d.ts +10 -0
  277. package/dist/core/templates/workflows/bulk-archive-change.js +493 -0
  278. package/dist/core/templates/workflows/comprehension-guidance.d.ts +9 -0
  279. package/dist/core/templates/workflows/comprehension-guidance.js +58 -0
  280. package/dist/core/templates/workflows/continue-change.d.ts +10 -0
  281. package/dist/core/templates/workflows/continue-change.js +239 -0
  282. package/dist/core/templates/workflows/explore.d.ts +10 -0
  283. package/dist/core/templates/workflows/explore.js +464 -0
  284. package/dist/core/templates/workflows/feedback.d.ts +9 -0
  285. package/dist/core/templates/workflows/feedback.js +108 -0
  286. package/dist/core/templates/workflows/ff-change.d.ts +10 -0
  287. package/dist/core/templates/workflows/ff-change.js +205 -0
  288. package/dist/core/templates/workflows/mcp-guidance.d.ts +13 -0
  289. package/dist/core/templates/workflows/mcp-guidance.js +116 -0
  290. package/dist/core/templates/workflows/new-change.d.ts +10 -0
  291. package/dist/core/templates/workflows/new-change.js +148 -0
  292. package/dist/core/templates/workflows/onboard.d.ts +10 -0
  293. package/dist/core/templates/workflows/onboard.js +566 -0
  294. package/dist/core/templates/workflows/propose.d.ts +10 -0
  295. package/dist/core/templates/workflows/propose.js +228 -0
  296. package/dist/core/templates/workflows/store-selection.d.ts +8 -0
  297. package/dist/core/templates/workflows/store-selection.js +8 -0
  298. package/dist/core/templates/workflows/sync-specs.d.ts +10 -0
  299. package/dist/core/templates/workflows/sync-specs.js +291 -0
  300. package/dist/core/templates/workflows/verify-change.d.ts +10 -0
  301. package/dist/core/templates/workflows/verify-change.js +346 -0
  302. package/dist/core/update.d.ts +82 -0
  303. package/dist/core/update.js +557 -0
  304. package/dist/core/validation/constants.d.ts +34 -0
  305. package/dist/core/validation/constants.js +40 -0
  306. package/dist/core/validation/types.d.ts +18 -0
  307. package/dist/core/validation/types.js +2 -0
  308. package/dist/core/validation/validator.d.ts +44 -0
  309. package/dist/core/validation/validator.js +435 -0
  310. package/dist/core/view.d.ts +8 -0
  311. package/dist/core/view.js +168 -0
  312. package/dist/core/working-set.d.ts +47 -0
  313. package/dist/core/working-set.js +43 -0
  314. package/dist/core/worksets.d.ts +75 -0
  315. package/dist/core/worksets.js +245 -0
  316. package/dist/core/zod-issues.d.ts +4 -0
  317. package/dist/core/zod-issues.js +10 -0
  318. package/dist/index.d.ts +3 -0
  319. package/dist/index.js +3 -0
  320. package/dist/prompts/searchable-multi-select.d.ts +28 -0
  321. package/dist/prompts/searchable-multi-select.js +159 -0
  322. package/dist/telemetry/config.d.ts +38 -0
  323. package/dist/telemetry/config.js +136 -0
  324. package/dist/telemetry/index.d.ts +31 -0
  325. package/dist/telemetry/index.js +164 -0
  326. package/dist/ui/ascii-patterns.d.ts +16 -0
  327. package/dist/ui/ascii-patterns.js +133 -0
  328. package/dist/ui/welcome-screen.d.ts +10 -0
  329. package/dist/ui/welcome-screen.js +146 -0
  330. package/dist/utils/change-metadata.d.ts +55 -0
  331. package/dist/utils/change-metadata.js +141 -0
  332. package/dist/utils/change-utils.d.ts +71 -0
  333. package/dist/utils/change-utils.js +138 -0
  334. package/dist/utils/command-references.d.ts +18 -0
  335. package/dist/utils/command-references.js +20 -0
  336. package/dist/utils/file-system.d.ts +41 -0
  337. package/dist/utils/file-system.js +320 -0
  338. package/dist/utils/index.d.ts +6 -0
  339. package/dist/utils/index.js +9 -0
  340. package/dist/utils/interactive.d.ts +18 -0
  341. package/dist/utils/interactive.js +21 -0
  342. package/dist/utils/item-discovery.d.ts +4 -0
  343. package/dist/utils/item-discovery.js +72 -0
  344. package/dist/utils/match.d.ts +3 -0
  345. package/dist/utils/match.js +22 -0
  346. package/dist/utils/shell-detection.d.ts +20 -0
  347. package/dist/utils/shell-detection.js +41 -0
  348. package/dist/utils/task-progress.d.ts +8 -0
  349. package/dist/utils/task-progress.js +36 -0
  350. package/package.json +84 -0
  351. package/schemas/spec-driven/schema.yaml +153 -0
  352. package/schemas/spec-driven/templates/design.md +19 -0
  353. package/schemas/spec-driven/templates/proposal.md +23 -0
  354. package/schemas/spec-driven/templates/spec.md +8 -0
  355. package/schemas/spec-driven/templates/tasks.md +9 -0
  356. package/scripts/postinstall.js +83 -0
@@ -0,0 +1,415 @@
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
+ /**
6
+ * Installer for PowerShell completion scripts.
7
+ * Works with both Windows PowerShell 5.1 and PowerShell Core 7+
8
+ */
9
+ export class PowerShellInstaller {
10
+ homeDir;
11
+ /**
12
+ * Markers for PowerShell profile configuration management
13
+ */
14
+ PROFILE_MARKERS = {
15
+ start: '# OPENSPEC:START',
16
+ end: '# OPENSPEC:END',
17
+ };
18
+ constructor(homeDir = os.homedir()) {
19
+ this.homeDir = homeDir;
20
+ }
21
+ /**
22
+ * Detect the encoding of a file by inspecting its BOM (Byte Order Mark).
23
+ * Returns the Node.js BufferEncoding and the raw BOM bytes to preserve on write.
24
+ */
25
+ detectEncoding(buffer) {
26
+ // UTF-16 LE BOM: FF FE
27
+ if (buffer.length >= 2 && buffer[0] === 0xff && buffer[1] === 0xfe) {
28
+ return { encoding: 'utf16le', bom: Buffer.from([0xff, 0xfe]) };
29
+ }
30
+ // UTF-16 BE BOM: FE FF — not natively supported by Node
31
+ if (buffer.length >= 2 && buffer[0] === 0xfe && buffer[1] === 0xff) {
32
+ throw new Error('File is encoded as UTF-16 BE which is not supported. ' +
33
+ 'Please re-save as UTF-8 or UTF-16 LE, then retry.');
34
+ }
35
+ // UTF-8 BOM: EF BB BF
36
+ if (buffer.length >= 3 && buffer[0] === 0xef && buffer[1] === 0xbb && buffer[2] === 0xbf) {
37
+ return { encoding: 'utf-8', bom: Buffer.from([0xef, 0xbb, 0xbf]) };
38
+ }
39
+ // No BOM → default UTF-8
40
+ return { encoding: 'utf-8', bom: Buffer.alloc(0) };
41
+ }
42
+ /**
43
+ * Read a profile file, preserving its encoding metadata for round-trip writes.
44
+ * Throws if the file uses UTF-16 BE (unsupported by Node).
45
+ */
46
+ async readProfileFile(filePath) {
47
+ const raw = await fs.readFile(filePath);
48
+ const { encoding, bom } = this.detectEncoding(raw);
49
+ const content = raw.subarray(bom.length).toString(encoding);
50
+ return { content, encoding, bom };
51
+ }
52
+ /**
53
+ * Write a profile file, preserving the original BOM and encoding.
54
+ */
55
+ async writeProfileFile(filePath, content, encoding, bom) {
56
+ const body = Buffer.from(content, encoding);
57
+ await fs.writeFile(filePath, Buffer.concat([bom, body]));
58
+ }
59
+ /**
60
+ * Get PowerShell profile path
61
+ * Prefers $PROFILE environment variable, falls back to platform defaults
62
+ *
63
+ * @returns Profile path
64
+ */
65
+ getProfilePath() {
66
+ // Check $PROFILE environment variable (set when running in PowerShell)
67
+ if (process.env.PROFILE) {
68
+ return process.env.PROFILE;
69
+ }
70
+ // Fall back to platform-specific defaults
71
+ if (process.platform === 'win32') {
72
+ // Windows: Documents/PowerShell/Microsoft.PowerShell_profile.ps1
73
+ return path.join(this.homeDir, 'Documents', 'PowerShell', 'Microsoft.PowerShell_profile.ps1');
74
+ }
75
+ else {
76
+ // macOS/Linux: .config/powershell/Microsoft.PowerShell_profile.ps1
77
+ return path.join(this.homeDir, '.config', 'powershell', 'Microsoft.PowerShell_profile.ps1');
78
+ }
79
+ }
80
+ /**
81
+ * Get all PowerShell profile paths to configure.
82
+ * On Windows, returns both PowerShell Core and Windows PowerShell 5.1 paths.
83
+ * On Unix, returns PowerShell Core path only.
84
+ */
85
+ getAllProfilePaths() {
86
+ // If PROFILE env var is set, use only that path
87
+ if (process.env.PROFILE) {
88
+ return [process.env.PROFILE];
89
+ }
90
+ if (process.platform === 'win32') {
91
+ return [
92
+ // PowerShell Core 6+ (cross-platform)
93
+ path.join(this.homeDir, 'Documents', 'PowerShell', 'Microsoft.PowerShell_profile.ps1'),
94
+ // Windows PowerShell 5.1 (Windows-only)
95
+ path.join(this.homeDir, 'Documents', 'WindowsPowerShell', 'Microsoft.PowerShell_profile.ps1'),
96
+ ];
97
+ }
98
+ else {
99
+ // Unix systems: PowerShell Core only
100
+ return [path.join(this.homeDir, '.config', 'powershell', 'Microsoft.PowerShell_profile.ps1')];
101
+ }
102
+ }
103
+ /**
104
+ * Get the installation path for the completion script
105
+ *
106
+ * @returns Installation path
107
+ */
108
+ getInstallationPath() {
109
+ const profilePath = this.getProfilePath();
110
+ const profileDir = path.dirname(profilePath);
111
+ return path.join(profileDir, 'OpenSpecCompletion.ps1');
112
+ }
113
+ /**
114
+ * Backup an existing completion file if it exists
115
+ *
116
+ * @param targetPath - Path to the file to backup
117
+ * @returns Path to the backup file, or undefined if no backup was needed
118
+ */
119
+ async backupExistingFile(targetPath) {
120
+ try {
121
+ await fs.access(targetPath);
122
+ // File exists, create a backup
123
+ const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
124
+ const backupPath = `${targetPath}.backup-${timestamp}`;
125
+ await fs.copyFile(targetPath, backupPath);
126
+ return backupPath;
127
+ }
128
+ catch {
129
+ // File doesn't exist, no backup needed
130
+ return undefined;
131
+ }
132
+ }
133
+ /**
134
+ * Generate PowerShell profile configuration content
135
+ *
136
+ * @param scriptPath - Path to the completion script
137
+ * @returns Configuration content
138
+ */
139
+ generateProfileConfig(scriptPath) {
140
+ return [
141
+ '# OpenSpec shell completions configuration',
142
+ `if (Test-Path "${scriptPath}") {`,
143
+ ` . "${scriptPath}"`,
144
+ '}',
145
+ ].join('\n');
146
+ }
147
+ /**
148
+ * Configure PowerShell profile to source the completion script
149
+ *
150
+ * @param scriptPath - Path to the completion script
151
+ * @returns true if configured successfully, false otherwise
152
+ */
153
+ async configureProfile(scriptPath) {
154
+ const profilePaths = this.getAllProfilePaths();
155
+ let anyConfigured = false;
156
+ for (const profilePath of profilePaths) {
157
+ try {
158
+ const profileDir = path.dirname(profilePath);
159
+ let profileExists = false;
160
+ try {
161
+ await fs.access(profilePath);
162
+ profileExists = true;
163
+ }
164
+ catch (err) {
165
+ if (err?.code !== 'ENOENT') {
166
+ throw err;
167
+ }
168
+ }
169
+ if (!profileExists) {
170
+ if (!(await FileSystemUtils.canWriteFile(profilePath))) {
171
+ throw new Error(`Path is not writable: ${profilePath}`);
172
+ }
173
+ await fs.mkdir(profileDir, { recursive: true });
174
+ }
175
+ let profileContent = '';
176
+ let fileEncoding = 'utf-8';
177
+ let fileBom = Buffer.alloc(0);
178
+ try {
179
+ const file = await this.readProfileFile(profilePath);
180
+ profileContent = file.content;
181
+ fileEncoding = file.encoding;
182
+ fileBom = file.bom;
183
+ }
184
+ catch (err) {
185
+ // If the file doesn't exist that's fine — we'll create it as UTF-8.
186
+ // Any other read error (permissions, unsupported encoding, etc.) → skip this profile.
187
+ if (err?.code === 'ENOENT') {
188
+ // keep defaults
189
+ }
190
+ else {
191
+ console.warn(`Warning: Skipping ${profilePath}: ${err?.message ?? String(err)}`);
192
+ continue;
193
+ }
194
+ }
195
+ // Check if already configured
196
+ const scriptLine = `. "${scriptPath}"`;
197
+ if (profileContent.includes(scriptLine)) {
198
+ continue; // Already configured, skip
199
+ }
200
+ // Add OpenSpec completion configuration with markers
201
+ const openspecBlock = [
202
+ '',
203
+ '# OPENSPEC:START - OpenSpec completion (managed block, do not edit manually)',
204
+ scriptLine,
205
+ '# OPENSPEC:END',
206
+ '',
207
+ ].join('\n');
208
+ const newContent = profileContent + openspecBlock;
209
+ if (!(await FileSystemUtils.canWriteFile(profilePath))) {
210
+ throw new Error(`Path is not writable: ${profilePath}`);
211
+ }
212
+ await this.writeProfileFile(profilePath, newContent, fileEncoding, fileBom);
213
+ anyConfigured = true;
214
+ }
215
+ catch (error) {
216
+ // Continue to next profile if this one fails
217
+ console.warn(`Warning: Could not configure ${profilePath}: ${error}`);
218
+ }
219
+ }
220
+ return anyConfigured;
221
+ }
222
+ /**
223
+ * Remove PowerShell profile configuration
224
+ * Used during uninstallation
225
+ *
226
+ * @returns true if removed successfully, false otherwise
227
+ */
228
+ async removeProfileConfig() {
229
+ const profilePaths = this.getAllProfilePaths();
230
+ let anyRemoved = false;
231
+ for (const profilePath of profilePaths) {
232
+ try {
233
+ // Read profile content with encoding detection
234
+ let profileContent;
235
+ let fileEncoding = 'utf-8';
236
+ let fileBom = Buffer.alloc(0);
237
+ try {
238
+ const file = await this.readProfileFile(profilePath);
239
+ profileContent = file.content;
240
+ fileEncoding = file.encoding;
241
+ fileBom = file.bom;
242
+ }
243
+ catch (err) {
244
+ if (err?.code === 'ENOENT') {
245
+ continue; // Profile doesn't exist, nothing to remove
246
+ }
247
+ console.warn(`Warning: Could not read ${profilePath}: ${err?.message ?? String(err)}`);
248
+ continue;
249
+ }
250
+ // Remove OPENSPEC:START -> OPENSPEC:END block
251
+ const startMarker = '# OPENSPEC:START';
252
+ const endMarker = '# OPENSPEC:END';
253
+ const startIndex = profileContent.indexOf(startMarker);
254
+ if (startIndex === -1) {
255
+ continue; // No OpenSpec block found
256
+ }
257
+ const endIndex = profileContent.indexOf(endMarker, startIndex);
258
+ if (endIndex === -1) {
259
+ console.warn(`Warning: Found start marker but no end marker in ${profilePath}`);
260
+ continue;
261
+ }
262
+ // Remove the block (including markers and surrounding newlines)
263
+ const beforeBlock = profileContent.substring(0, startIndex);
264
+ const afterBlock = profileContent.substring(endIndex + endMarker.length);
265
+ // Clean up extra newlines
266
+ const newContent = (beforeBlock.trimEnd() + '\n' + afterBlock.trimStart()).trim() + '\n';
267
+ if (!(await FileSystemUtils.canWriteFile(profilePath))) {
268
+ throw new Error(`Path is not writable: ${profilePath}`);
269
+ }
270
+ await this.writeProfileFile(profilePath, newContent, fileEncoding, fileBom);
271
+ anyRemoved = true;
272
+ }
273
+ catch (error) {
274
+ console.warn(`Warning: Could not clean ${profilePath}: ${error}`);
275
+ }
276
+ }
277
+ return anyRemoved;
278
+ }
279
+ /**
280
+ * Install the completion script
281
+ *
282
+ * @param completionScript - The completion script content to install
283
+ * @returns Installation result with status and instructions
284
+ */
285
+ async install(completionScript) {
286
+ try {
287
+ const targetPath = this.getInstallationPath();
288
+ // Check if already installed with same content
289
+ let isUpdate = false;
290
+ try {
291
+ const existingContent = await fs.readFile(targetPath, 'utf-8');
292
+ if (existingContent === completionScript) {
293
+ // Already installed and up to date
294
+ return {
295
+ success: true,
296
+ installedPath: targetPath,
297
+ message: 'Completion script is already installed (up to date)',
298
+ instructions: [
299
+ 'The completion script is already installed and up to date.',
300
+ 'If completions are not working, try restarting PowerShell or run: . $PROFILE',
301
+ ],
302
+ };
303
+ }
304
+ // File exists but content is different - this is an update
305
+ isUpdate = true;
306
+ }
307
+ catch (error) {
308
+ // File doesn't exist or can't be read, proceed with installation
309
+ console.debug(`Unable to read existing completion file at ${targetPath}: ${error.message}`);
310
+ }
311
+ if (!(await FileSystemUtils.canWriteFile(targetPath))) {
312
+ throw new Error(`Path is not writable: ${targetPath}`);
313
+ }
314
+ // Ensure the directory exists
315
+ const targetDir = path.dirname(targetPath);
316
+ await fs.mkdir(targetDir, { recursive: true });
317
+ // Backup existing file if updating
318
+ const backupPath = isUpdate ? await this.backupExistingFile(targetPath) : undefined;
319
+ // Write the completion script
320
+ await fs.writeFile(targetPath, completionScript, 'utf-8');
321
+ // Auto-configure PowerShell profile
322
+ const profileConfigured = await this.configureProfile(targetPath);
323
+ // Generate instructions if profile wasn't auto-configured
324
+ const instructions = profileConfigured ? undefined : this.generateInstructions(targetPath);
325
+ // Determine appropriate message
326
+ let message;
327
+ if (isUpdate) {
328
+ message = backupPath
329
+ ? 'Completion script updated successfully (previous version backed up)'
330
+ : 'Completion script updated successfully';
331
+ }
332
+ else {
333
+ message = profileConfigured
334
+ ? 'Completion script installed and PowerShell profile configured successfully'
335
+ : 'Completion script installed successfully for PowerShell';
336
+ }
337
+ return {
338
+ success: true,
339
+ installedPath: targetPath,
340
+ backupPath,
341
+ profileConfigured,
342
+ message,
343
+ instructions,
344
+ };
345
+ }
346
+ catch (error) {
347
+ return {
348
+ success: false,
349
+ message: `Failed to install completion script: ${error instanceof Error ? error.message : String(error)}`,
350
+ };
351
+ }
352
+ }
353
+ /**
354
+ * Generate user instructions for enabling completions
355
+ *
356
+ * @param installedPath - Path where the script was installed
357
+ * @returns Array of instruction strings
358
+ */
359
+ generateInstructions(installedPath) {
360
+ const profilePath = this.getProfilePath();
361
+ return [
362
+ 'Completion script installed successfully.',
363
+ '',
364
+ `To enable completions, add the following to your PowerShell profile (${profilePath}):`,
365
+ '',
366
+ ' # Source OpenSpec completions',
367
+ ` if (Test-Path "${installedPath}") {`,
368
+ ` . "${installedPath}"`,
369
+ ' }',
370
+ '',
371
+ 'Then restart PowerShell or run: . $PROFILE',
372
+ ];
373
+ }
374
+ /**
375
+ * Uninstall the completion script
376
+ *
377
+ * @param options - Optional uninstall options
378
+ * @param options.yes - Skip confirmation prompt (handled by command layer)
379
+ * @returns Uninstallation result
380
+ */
381
+ async uninstall(options) {
382
+ try {
383
+ const targetPath = this.getInstallationPath();
384
+ // Check if installed
385
+ try {
386
+ await fs.access(targetPath);
387
+ }
388
+ catch {
389
+ return {
390
+ success: false,
391
+ message: 'Completion script is not installed',
392
+ };
393
+ }
394
+ const targetDir = path.dirname(targetPath);
395
+ if (!(await FileSystemUtils.canWriteFile(targetDir))) {
396
+ throw new Error(`Path is not writable: ${targetDir}`);
397
+ }
398
+ // Remove the completion script
399
+ await fs.unlink(targetPath);
400
+ // Remove profile configuration
401
+ await this.removeProfileConfig();
402
+ return {
403
+ success: true,
404
+ message: 'Completion script uninstalled successfully',
405
+ };
406
+ }
407
+ catch (error) {
408
+ return {
409
+ success: false,
410
+ message: `Failed to uninstall completion script: ${error instanceof Error ? error.message : String(error)}`,
411
+ };
412
+ }
413
+ }
414
+ }
415
+ //# sourceMappingURL=powershell-installer.js.map
@@ -0,0 +1,117 @@
1
+ import { InstallationResult } from '../factory.js';
2
+ /**
3
+ * Installer for Zsh completion scripts.
4
+ * Supports both Oh My Zsh and standard Zsh configurations.
5
+ */
6
+ export declare class ZshInstaller {
7
+ private readonly homeDir;
8
+ /**
9
+ * Markers for .zshrc configuration management
10
+ */
11
+ private readonly ZSHRC_MARKERS;
12
+ constructor(homeDir?: string);
13
+ /**
14
+ * Check if Oh My Zsh is installed
15
+ *
16
+ * @returns true if Oh My Zsh is detected via $ZSH env var or directory exists
17
+ */
18
+ isOhMyZshInstalled(): Promise<boolean>;
19
+ /**
20
+ * Get the appropriate installation path for the completion script
21
+ *
22
+ * @returns Object with installation path and whether it's Oh My Zsh
23
+ */
24
+ getInstallationPath(): Promise<{
25
+ path: string;
26
+ isOhMyZsh: boolean;
27
+ }>;
28
+ /**
29
+ * Backup an existing completion file if it exists
30
+ *
31
+ * @param targetPath - Path to the file to backup
32
+ * @returns Path to the backup file, or undefined if no backup was needed
33
+ */
34
+ backupExistingFile(targetPath: string): Promise<string | undefined>;
35
+ /**
36
+ * Get the path to .zshrc file
37
+ *
38
+ * @returns Path to .zshrc
39
+ */
40
+ private getZshrcPath;
41
+ /**
42
+ * Generate .zshrc configuration content
43
+ *
44
+ * @param completionsDir - Directory containing completion scripts
45
+ * @returns Configuration content
46
+ */
47
+ private generateZshrcConfig;
48
+ /**
49
+ * Configure .zshrc to enable completions
50
+ * Only applies to standard Zsh (not Oh My Zsh)
51
+ *
52
+ * @param completionsDir - Directory containing completion scripts
53
+ * @returns true if configured successfully, false otherwise
54
+ */
55
+ configureZshrc(completionsDir: string): Promise<boolean>;
56
+ /**
57
+ * Check if .zshrc has OpenSpec configuration markers
58
+ *
59
+ * @returns true if .zshrc exists and has markers
60
+ */
61
+ private hasZshrcConfig;
62
+ /**
63
+ * Remove .zshrc configuration
64
+ * Used during uninstallation
65
+ *
66
+ * @returns true if removed successfully, false otherwise
67
+ */
68
+ removeZshrcConfig(): Promise<boolean>;
69
+ /**
70
+ * Install the completion script
71
+ *
72
+ * @param completionScript - The completion script content to install
73
+ * @returns Installation result with status and instructions
74
+ */
75
+ install(completionScript: string): Promise<InstallationResult>;
76
+ /**
77
+ * Generate Oh My Zsh fpath verification guidance
78
+ *
79
+ * @param completionsDir - Custom completions directory path
80
+ * @returns Array of guidance strings, or undefined if not needed
81
+ */
82
+ private generateOhMyZshFpathGuidance;
83
+ /**
84
+ * Generate user instructions for enabling completions
85
+ *
86
+ * @param isOhMyZsh - Whether Oh My Zsh is being used
87
+ * @param installedPath - Path where the script was installed
88
+ * @returns Array of instruction strings
89
+ */
90
+ private generateInstructions;
91
+ /**
92
+ * Uninstall the completion script
93
+ *
94
+ * @returns true if uninstalled successfully, false otherwise
95
+ */
96
+ uninstall(): Promise<{
97
+ success: boolean;
98
+ message: string;
99
+ }>;
100
+ /**
101
+ * Check if completion script is currently installed
102
+ *
103
+ * @returns true if the completion script exists
104
+ */
105
+ isInstalled(): Promise<boolean>;
106
+ /**
107
+ * Get information about the current installation
108
+ *
109
+ * @returns Installation status information
110
+ */
111
+ getInstallationInfo(): Promise<{
112
+ installed: boolean;
113
+ path?: string;
114
+ isOhMyZsh?: boolean;
115
+ }>;
116
+ }
117
+ //# sourceMappingURL=zsh-installer.d.ts.map