@fission-ai/openspec 0.23.0 → 1.0.0

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 (183) hide show
  1. package/README.md +111 -382
  2. package/dist/cli/index.js +120 -6
  3. package/dist/commands/workflow/index.d.ts +17 -0
  4. package/dist/commands/workflow/index.js +12 -0
  5. package/dist/commands/workflow/instructions.d.ts +29 -0
  6. package/dist/commands/workflow/instructions.js +381 -0
  7. package/dist/commands/workflow/new-change.d.ts +11 -0
  8. package/dist/commands/workflow/new-change.js +44 -0
  9. package/dist/commands/workflow/schemas.d.ts +10 -0
  10. package/dist/commands/workflow/schemas.js +34 -0
  11. package/dist/commands/workflow/shared.d.ts +52 -0
  12. package/dist/commands/workflow/shared.js +111 -0
  13. package/dist/commands/workflow/status.d.ts +14 -0
  14. package/dist/commands/workflow/status.js +58 -0
  15. package/dist/commands/workflow/templates.d.ts +16 -0
  16. package/dist/commands/workflow/templates.js +68 -0
  17. package/dist/core/artifact-graph/instruction-loader.d.ts +5 -1
  18. package/dist/core/artifact-graph/instruction-loader.js +8 -19
  19. package/dist/core/command-generation/adapters/amazon-q.d.ts +13 -0
  20. package/dist/core/command-generation/adapters/amazon-q.js +26 -0
  21. package/dist/core/command-generation/adapters/antigravity.d.ts +13 -0
  22. package/dist/core/command-generation/adapters/antigravity.js +26 -0
  23. package/dist/core/command-generation/adapters/auggie.d.ts +13 -0
  24. package/dist/core/command-generation/adapters/auggie.js +27 -0
  25. package/dist/core/command-generation/adapters/claude.d.ts +13 -0
  26. package/dist/core/command-generation/adapters/claude.js +50 -0
  27. package/dist/core/command-generation/adapters/cline.d.ts +14 -0
  28. package/dist/core/command-generation/adapters/cline.js +27 -0
  29. package/dist/core/command-generation/adapters/codebuddy.d.ts +13 -0
  30. package/dist/core/command-generation/adapters/codebuddy.js +28 -0
  31. package/dist/core/command-generation/adapters/codex.d.ts +13 -0
  32. package/dist/core/command-generation/adapters/codex.js +27 -0
  33. package/dist/core/command-generation/adapters/continue.d.ts +13 -0
  34. package/dist/core/command-generation/adapters/continue.js +28 -0
  35. package/dist/core/command-generation/adapters/costrict.d.ts +13 -0
  36. package/dist/core/command-generation/adapters/costrict.js +27 -0
  37. package/dist/core/command-generation/adapters/crush.d.ts +13 -0
  38. package/dist/core/command-generation/adapters/crush.js +30 -0
  39. package/dist/core/command-generation/adapters/cursor.d.ts +14 -0
  40. package/dist/core/command-generation/adapters/cursor.js +44 -0
  41. package/dist/core/command-generation/adapters/factory.d.ts +13 -0
  42. package/dist/core/command-generation/adapters/factory.js +27 -0
  43. package/dist/core/command-generation/adapters/gemini.d.ts +13 -0
  44. package/dist/core/command-generation/adapters/gemini.js +26 -0
  45. package/dist/core/command-generation/adapters/github-copilot.d.ts +13 -0
  46. package/dist/core/command-generation/adapters/github-copilot.js +26 -0
  47. package/dist/core/command-generation/adapters/iflow.d.ts +13 -0
  48. package/dist/core/command-generation/adapters/iflow.js +29 -0
  49. package/dist/core/command-generation/adapters/index.d.ts +27 -0
  50. package/dist/core/command-generation/adapters/index.js +27 -0
  51. package/dist/core/command-generation/adapters/kilocode.d.ts +14 -0
  52. package/dist/core/command-generation/adapters/kilocode.js +23 -0
  53. package/dist/core/command-generation/adapters/opencode.d.ts +13 -0
  54. package/dist/core/command-generation/adapters/opencode.js +26 -0
  55. package/dist/core/command-generation/adapters/qoder.d.ts +13 -0
  56. package/dist/core/command-generation/adapters/qoder.js +30 -0
  57. package/dist/core/command-generation/adapters/qwen.d.ts +13 -0
  58. package/dist/core/command-generation/adapters/qwen.js +26 -0
  59. package/dist/core/command-generation/adapters/roocode.d.ts +14 -0
  60. package/dist/core/command-generation/adapters/roocode.js +27 -0
  61. package/dist/core/command-generation/adapters/windsurf.d.ts +14 -0
  62. package/dist/core/command-generation/adapters/windsurf.js +51 -0
  63. package/dist/core/command-generation/generator.d.ts +21 -0
  64. package/dist/core/command-generation/generator.js +27 -0
  65. package/dist/core/command-generation/index.d.ts +22 -0
  66. package/dist/core/command-generation/index.js +24 -0
  67. package/dist/core/command-generation/registry.d.ts +36 -0
  68. package/dist/core/command-generation/registry.js +88 -0
  69. package/dist/core/command-generation/types.d.ts +55 -0
  70. package/dist/core/command-generation/types.js +8 -0
  71. package/dist/core/config.d.ts +1 -0
  72. package/dist/core/config.js +21 -21
  73. package/dist/core/init.d.ts +16 -36
  74. package/dist/core/init.js +323 -534
  75. package/dist/core/legacy-cleanup.d.ts +162 -0
  76. package/dist/core/legacy-cleanup.js +501 -0
  77. package/dist/core/shared/index.d.ts +8 -0
  78. package/dist/core/shared/index.js +8 -0
  79. package/dist/core/shared/skill-generation.d.ts +41 -0
  80. package/dist/core/shared/skill-generation.js +76 -0
  81. package/dist/core/shared/tool-detection.d.ts +66 -0
  82. package/dist/core/shared/tool-detection.js +140 -0
  83. package/dist/core/templates/index.d.ts +7 -16
  84. package/dist/core/templates/index.js +8 -36
  85. package/dist/core/templates/skill-templates.d.ts +13 -0
  86. package/dist/core/templates/skill-templates.js +627 -21
  87. package/dist/core/update.d.ts +38 -0
  88. package/dist/core/update.js +280 -62
  89. package/dist/prompts/searchable-multi-select.d.ts +27 -0
  90. package/dist/prompts/searchable-multi-select.js +149 -0
  91. package/dist/ui/ascii-patterns.d.ts +16 -0
  92. package/dist/ui/ascii-patterns.js +133 -0
  93. package/dist/ui/welcome-screen.d.ts +10 -0
  94. package/dist/ui/welcome-screen.js +146 -0
  95. package/dist/utils/file-system.d.ts +11 -0
  96. package/dist/utils/file-system.js +65 -2
  97. package/dist/utils/index.d.ts +1 -0
  98. package/dist/utils/index.js +2 -0
  99. package/package.json +1 -1
  100. package/dist/commands/artifact-workflow.d.ts +0 -17
  101. package/dist/commands/artifact-workflow.js +0 -915
  102. package/dist/core/configurators/agents.d.ts +0 -8
  103. package/dist/core/configurators/agents.js +0 -15
  104. package/dist/core/configurators/base.d.ts +0 -7
  105. package/dist/core/configurators/base.js +0 -2
  106. package/dist/core/configurators/claude.d.ts +0 -8
  107. package/dist/core/configurators/claude.js +0 -15
  108. package/dist/core/configurators/cline.d.ts +0 -8
  109. package/dist/core/configurators/cline.js +0 -15
  110. package/dist/core/configurators/codebuddy.d.ts +0 -8
  111. package/dist/core/configurators/codebuddy.js +0 -15
  112. package/dist/core/configurators/costrict.d.ts +0 -8
  113. package/dist/core/configurators/costrict.js +0 -15
  114. package/dist/core/configurators/iflow.d.ts +0 -8
  115. package/dist/core/configurators/iflow.js +0 -15
  116. package/dist/core/configurators/qoder.d.ts +0 -30
  117. package/dist/core/configurators/qoder.js +0 -42
  118. package/dist/core/configurators/qwen.d.ts +0 -24
  119. package/dist/core/configurators/qwen.js +0 -37
  120. package/dist/core/configurators/registry.d.ts +0 -9
  121. package/dist/core/configurators/registry.js +0 -43
  122. package/dist/core/configurators/slash/amazon-q.d.ts +0 -9
  123. package/dist/core/configurators/slash/amazon-q.js +0 -46
  124. package/dist/core/configurators/slash/antigravity.d.ts +0 -9
  125. package/dist/core/configurators/slash/antigravity.js +0 -23
  126. package/dist/core/configurators/slash/auggie.d.ts +0 -9
  127. package/dist/core/configurators/slash/auggie.js +0 -31
  128. package/dist/core/configurators/slash/base.d.ts +0 -19
  129. package/dist/core/configurators/slash/base.js +0 -69
  130. package/dist/core/configurators/slash/claude.d.ts +0 -9
  131. package/dist/core/configurators/slash/claude.js +0 -37
  132. package/dist/core/configurators/slash/cline.d.ts +0 -9
  133. package/dist/core/configurators/slash/cline.js +0 -23
  134. package/dist/core/configurators/slash/codebuddy.d.ts +0 -9
  135. package/dist/core/configurators/slash/codebuddy.js +0 -34
  136. package/dist/core/configurators/slash/codex.d.ts +0 -14
  137. package/dist/core/configurators/slash/codex.js +0 -109
  138. package/dist/core/configurators/slash/continue.d.ts +0 -9
  139. package/dist/core/configurators/slash/continue.js +0 -46
  140. package/dist/core/configurators/slash/costrict.d.ts +0 -9
  141. package/dist/core/configurators/slash/costrict.js +0 -31
  142. package/dist/core/configurators/slash/crush.d.ts +0 -9
  143. package/dist/core/configurators/slash/crush.js +0 -37
  144. package/dist/core/configurators/slash/cursor.d.ts +0 -9
  145. package/dist/core/configurators/slash/cursor.js +0 -37
  146. package/dist/core/configurators/slash/factory.d.ts +0 -10
  147. package/dist/core/configurators/slash/factory.js +0 -35
  148. package/dist/core/configurators/slash/gemini.d.ts +0 -9
  149. package/dist/core/configurators/slash/gemini.js +0 -22
  150. package/dist/core/configurators/slash/github-copilot.d.ts +0 -9
  151. package/dist/core/configurators/slash/github-copilot.js +0 -34
  152. package/dist/core/configurators/slash/iflow.d.ts +0 -9
  153. package/dist/core/configurators/slash/iflow.js +0 -37
  154. package/dist/core/configurators/slash/kilocode.d.ts +0 -9
  155. package/dist/core/configurators/slash/kilocode.js +0 -17
  156. package/dist/core/configurators/slash/opencode.d.ts +0 -12
  157. package/dist/core/configurators/slash/opencode.js +0 -72
  158. package/dist/core/configurators/slash/qoder.d.ts +0 -35
  159. package/dist/core/configurators/slash/qoder.js +0 -76
  160. package/dist/core/configurators/slash/qwen.d.ts +0 -32
  161. package/dist/core/configurators/slash/qwen.js +0 -49
  162. package/dist/core/configurators/slash/registry.d.ts +0 -8
  163. package/dist/core/configurators/slash/registry.js +0 -78
  164. package/dist/core/configurators/slash/roocode.d.ts +0 -9
  165. package/dist/core/configurators/slash/roocode.js +0 -23
  166. package/dist/core/configurators/slash/toml-base.d.ts +0 -10
  167. package/dist/core/configurators/slash/toml-base.js +0 -53
  168. package/dist/core/configurators/slash/windsurf.d.ts +0 -9
  169. package/dist/core/configurators/slash/windsurf.js +0 -23
  170. package/dist/core/templates/agents-root-stub.d.ts +0 -2
  171. package/dist/core/templates/agents-root-stub.js +0 -17
  172. package/dist/core/templates/agents-template.d.ts +0 -2
  173. package/dist/core/templates/agents-template.js +0 -458
  174. package/dist/core/templates/claude-template.d.ts +0 -2
  175. package/dist/core/templates/claude-template.js +0 -2
  176. package/dist/core/templates/cline-template.d.ts +0 -2
  177. package/dist/core/templates/cline-template.js +0 -2
  178. package/dist/core/templates/costrict-template.d.ts +0 -2
  179. package/dist/core/templates/costrict-template.js +0 -2
  180. package/dist/core/templates/project-template.d.ts +0 -8
  181. package/dist/core/templates/project-template.js +0 -32
  182. package/dist/core/templates/slash-command-templates.d.ts +0 -4
  183. package/dist/core/templates/slash-command-templates.js +0 -49
@@ -0,0 +1,133 @@
1
+ /**
2
+ * ASCII art animation patterns for the welcome screen.
3
+ * OpenSpec logo animation - diamond/rhombus shape with hollow center "O".
4
+ */
5
+ // Detect if full Unicode is supported
6
+ const supportsUnicode = process.platform !== 'win32' ||
7
+ !!process.env.WT_SESSION || // Windows Terminal
8
+ !!process.env.TERM_PROGRAM; // Modern terminal
9
+ // Character set based on Unicode support
10
+ // Block characters for pixel-art aesthetic
11
+ const CHARS = supportsUnicode
12
+ ? { full: '██', dim: '░░', empty: ' ' }
13
+ : { full: '##', dim: '++', empty: ' ' };
14
+ const _ = CHARS.empty;
15
+ const F = CHARS.full;
16
+ const D = CHARS.dim;
17
+ /**
18
+ * Welcome animation frames - OpenSpec logo building from center
19
+ * 7 rows × 6 columns diamond with hollow center "O"
20
+ * Center bar is 2 cols × 3 rows (rows 3,4,5 cols 3,4)
21
+ * Each frame is an array of strings (lines of ASCII art)
22
+ * Grid: 6 cols × 2 chars = 12 chars wide
23
+ */
24
+ export const WELCOME_ANIMATION = {
25
+ interval: 120,
26
+ frames: [
27
+ // Frame 1: Empty
28
+ [
29
+ `${_}${_}${_}${_}${_}${_}${_}${_}`, // padding row 1
30
+ `${_}${_}${_}${_}${_}${_}${_}${_}`, // padding row 2
31
+ `${_}${_}${_}${_}${_}${_}${_}${_}`, // padding row 3
32
+ `${_}${_}${_}${_}${_}${_}${_}${_}`,
33
+ `${_}${_}${_}${_}${_}${_}${_}${_}`,
34
+ `${_}${_}${_}${_}${_}${_}${_}${_}`,
35
+ `${_}${_}${_}${_}${_}${_}${_}${_}`,
36
+ `${_}${_}${_}${_}${_}${_}${_}${_}`,
37
+ `${_}${_}${_}${_}${_}${_}${_}${_}`,
38
+ `${_}${_}${_}${_}${_}${_}${_}${_}`,
39
+ ],
40
+ // Frame 2: Center blocks appear (dim) - 2x3 center bar
41
+ [
42
+ `${_}${_}${_}${_}${_}${_}${_}${_}`, // padding row 1
43
+ `${_}${_}${_}${_}${_}${_}${_}${_}`, // padding row 2
44
+ `${_}${_}${_}${_}${_}${_}${_}${_}`, // padding row 3
45
+ `${_}${_}${_}${_}${_}${_}${_}${_}`,
46
+ `${_}${_}${_}${_}${_}${_}${_}${_}`,
47
+ `${_}${_}${_}${_}${D}${D}${_}${_}`,
48
+ `${_}${_}${_}${_}${D}${D}${_}${_}`,
49
+ `${_}${_}${_}${_}${D}${D}${_}${_}`,
50
+ `${_}${_}${_}${_}${_}${_}${_}${_}`,
51
+ `${_}${_}${_}${_}${_}${_}${_}${_}`,
52
+ ],
53
+ // Frame 3: Center blocks solidify
54
+ [
55
+ `${_}${_}${_}${_}${_}${_}${_}${_}`, // padding row 1
56
+ `${_}${_}${_}${_}${_}${_}${_}${_}`, // padding row 2
57
+ `${_}${_}${_}${_}${_}${_}${_}${_}`, // padding row 3
58
+ `${_}${_}${_}${_}${_}${_}${_}${_}`,
59
+ `${_}${_}${_}${_}${_}${_}${_}${_}`,
60
+ `${_}${_}${_}${_}${F}${F}${_}${_}`,
61
+ `${_}${_}${_}${_}${F}${F}${_}${_}`,
62
+ `${_}${_}${_}${_}${F}${F}${_}${_}`,
63
+ `${_}${_}${_}${_}${_}${_}${_}${_}`,
64
+ `${_}${_}${_}${_}${_}${_}${_}${_}`,
65
+ ],
66
+ // Frame 4: Top and bottom points appear
67
+ [
68
+ `${_}${_}${_}${_}${_}${_}${_}${_}`, // padding row 1
69
+ `${_}${_}${_}${_}${_}${_}${_}${_}`, // padding row 2
70
+ `${_}${_}${_}${_}${_}${_}${_}${_}`, // padding row 3
71
+ `${_}${_}${_}${_}${D}${D}${_}${_}`,
72
+ `${_}${_}${_}${_}${_}${_}${_}${_}`,
73
+ `${_}${_}${_}${_}${F}${F}${_}${_}`,
74
+ `${_}${_}${_}${_}${F}${F}${_}${_}`,
75
+ `${_}${_}${_}${_}${F}${F}${_}${_}`,
76
+ `${_}${_}${_}${_}${_}${_}${_}${_}`,
77
+ `${_}${_}${_}${_}${D}${D}${_}${_}`,
78
+ ],
79
+ // Frame 5: Inner ring forming
80
+ [
81
+ `${_}${_}${_}${_}${_}${_}${_}${_}`, // padding row 1
82
+ `${_}${_}${_}${_}${_}${_}${_}${_}`, // padding row 2
83
+ `${_}${_}${_}${_}${_}${_}${_}${_}`, // padding row 3
84
+ `${_}${_}${_}${_}${F}${F}${_}${_}`,
85
+ `${_}${_}${_}${D}${_}${_}${D}${_}`,
86
+ `${_}${_}${_}${_}${F}${F}${_}${_}`,
87
+ `${_}${_}${_}${_}${F}${F}${_}${_}`,
88
+ `${_}${_}${_}${_}${F}${F}${_}${_}`,
89
+ `${_}${_}${_}${D}${_}${_}${D}${_}`,
90
+ `${_}${_}${_}${_}${F}${F}${_}${_}`,
91
+ ],
92
+ // Frame 6: Outer ring appearing
93
+ [
94
+ `${_}${_}${_}${_}${_}${_}${_}${_}`, // padding row 1
95
+ `${_}${_}${_}${_}${_}${_}${_}${_}`, // padding row 2
96
+ `${_}${_}${_}${_}${_}${_}${_}${_}`, // padding row 3
97
+ `${_}${_}${_}${_}${F}${F}${_}${_}`,
98
+ `${_}${_}${_}${F}${_}${_}${F}${_}`,
99
+ `${_}${_}${D}${_}${F}${F}${_}${D}`,
100
+ `${_}${_}${D}${_}${F}${F}${_}${D}`,
101
+ `${_}${_}${D}${_}${F}${F}${_}${D}`,
102
+ `${_}${_}${_}${F}${_}${_}${F}${_}`,
103
+ `${_}${_}${_}${_}${F}${F}${_}${_}`,
104
+ ],
105
+ // Frame 7: Full logo
106
+ [
107
+ `${_}${_}${_}${_}${_}${_}${_}${_}`, // padding row 1
108
+ `${_}${_}${_}${_}${_}${_}${_}${_}`, // padding row 2
109
+ `${_}${_}${_}${_}${_}${_}${_}${_}`, // padding row 3
110
+ `${_}${_}${_}${_}${F}${F}${_}${_}`,
111
+ `${_}${_}${_}${F}${_}${_}${F}${_}`,
112
+ `${_}${_}${F}${_}${F}${F}${_}${F}`,
113
+ `${_}${_}${F}${_}${F}${F}${_}${F}`,
114
+ `${_}${_}${F}${_}${F}${F}${_}${F}`,
115
+ `${_}${_}${_}${F}${_}${_}${F}${_}`,
116
+ `${_}${_}${_}${_}${F}${F}${_}${_}`,
117
+ ],
118
+ // Frame 8: Hold complete logo
119
+ [
120
+ `${_}${_}${_}${_}${_}${_}${_}${_}`, // padding row 1
121
+ `${_}${_}${_}${_}${_}${_}${_}${_}`, // padding row 2
122
+ `${_}${_}${_}${_}${_}${_}${_}${_}`, // padding row 3
123
+ `${_}${_}${_}${_}${F}${F}${_}${_}`,
124
+ `${_}${_}${_}${F}${_}${_}${F}${_}`,
125
+ `${_}${_}${F}${_}${F}${F}${_}${F}`,
126
+ `${_}${_}${F}${_}${F}${F}${_}${F}`,
127
+ `${_}${_}${F}${_}${F}${F}${_}${F}`,
128
+ `${_}${_}${_}${F}${_}${_}${F}${_}`,
129
+ `${_}${_}${_}${_}${F}${F}${_}${_}`,
130
+ ],
131
+ ],
132
+ };
133
+ //# sourceMappingURL=ascii-patterns.js.map
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Animated welcome screen for the experimental artifact workflow setup.
3
+ * Shows side-by-side layout with animated ASCII art on left and welcome text on right.
4
+ */
5
+ /**
6
+ * Shows the animated welcome screen.
7
+ * Returns when user presses Enter.
8
+ */
9
+ export declare function showWelcomeScreen(): Promise<void>;
10
+ //# sourceMappingURL=welcome-screen.d.ts.map
@@ -0,0 +1,146 @@
1
+ /**
2
+ * Animated welcome screen for the experimental artifact workflow setup.
3
+ * Shows side-by-side layout with animated ASCII art on left and welcome text on right.
4
+ */
5
+ import chalk from 'chalk';
6
+ import { WELCOME_ANIMATION } from './ascii-patterns.js';
7
+ // Minimum terminal width for side-by-side layout
8
+ const MIN_WIDTH = 60;
9
+ // Width of the ASCII art column (with padding)
10
+ const ART_COLUMN_WIDTH = 24;
11
+ /**
12
+ * Welcome text content (right column)
13
+ */
14
+ function getWelcomeText() {
15
+ return [
16
+ chalk.white.bold('Welcome to OpenSpec'),
17
+ chalk.dim('A lightweight spec-driven framework'),
18
+ '',
19
+ chalk.white('This setup will configure:'),
20
+ chalk.dim(' • Agent Skills for AI tools'),
21
+ chalk.dim(' • /opsx:* slash commands'),
22
+ '',
23
+ chalk.white('Quick start after setup:'),
24
+ ` ${chalk.yellow('/opsx:new')} ${chalk.dim('Create a change')}`,
25
+ ` ${chalk.yellow('/opsx:continue')} ${chalk.dim('Next artifact')}`,
26
+ ` ${chalk.yellow('/opsx:apply')} ${chalk.dim('Implement tasks')}`,
27
+ '',
28
+ chalk.cyan('Press Enter to select tools...'),
29
+ ];
30
+ }
31
+ /**
32
+ * Renders a single frame with side-by-side layout
33
+ */
34
+ function renderFrame(artLines, textLines) {
35
+ const maxLines = Math.max(artLines.length, textLines.length);
36
+ const lines = [];
37
+ for (let i = 0; i < maxLines; i++) {
38
+ const artLine = artLines[i] || '';
39
+ const textLine = textLines[i] || '';
40
+ // Pad the art column to fixed width
41
+ const paddedArt = artLine.padEnd(ART_COLUMN_WIDTH);
42
+ // Color the ASCII art with cyan for visual appeal
43
+ const coloredArt = chalk.cyan(paddedArt);
44
+ // Clear line before writing to prevent residual characters
45
+ lines.push(`\x1b[2K${coloredArt}${textLine}`);
46
+ }
47
+ return lines.join('\n');
48
+ }
49
+ /**
50
+ * Checks if the terminal supports animation
51
+ */
52
+ function canAnimate() {
53
+ // Must be TTY
54
+ if (!process.stdout.isTTY)
55
+ return false;
56
+ // Respect NO_COLOR
57
+ if (process.env.NO_COLOR)
58
+ return false;
59
+ // Check terminal width
60
+ const columns = process.stdout.columns || 80;
61
+ if (columns < MIN_WIDTH)
62
+ return false;
63
+ return true;
64
+ }
65
+ /**
66
+ * Wait for Enter key press
67
+ */
68
+ function waitForEnter() {
69
+ return new Promise((resolve) => {
70
+ const { stdin } = process;
71
+ // Handle non-TTY gracefully
72
+ if (!stdin.isTTY) {
73
+ resolve();
74
+ return;
75
+ }
76
+ const wasRaw = stdin.isRaw;
77
+ stdin.setRawMode(true);
78
+ stdin.resume();
79
+ const onData = (data) => {
80
+ const char = data.toString();
81
+ // Enter key or Ctrl+C
82
+ if (char === '\r' || char === '\n' || char === '\u0003') {
83
+ stdin.removeListener('data', onData);
84
+ stdin.setRawMode(wasRaw);
85
+ stdin.pause();
86
+ // Handle Ctrl+C
87
+ if (char === '\u0003') {
88
+ process.stdout.write('\n');
89
+ process.exit(0);
90
+ }
91
+ resolve();
92
+ }
93
+ };
94
+ stdin.on('data', onData);
95
+ });
96
+ }
97
+ /**
98
+ * Shows the animated welcome screen.
99
+ * Returns when user presses Enter.
100
+ */
101
+ export async function showWelcomeScreen() {
102
+ const textLines = getWelcomeText();
103
+ if (!canAnimate()) {
104
+ // Fallback: show static welcome
105
+ const frame = WELCOME_ANIMATION.frames[3]; // Peak frame
106
+ process.stdout.write('\n' + renderFrame(frame, textLines) + '\n\n');
107
+ return;
108
+ }
109
+ let frameIndex = 0;
110
+ let running = true;
111
+ let isFirstRender = true;
112
+ // Content height for cursor movement between frames
113
+ const numContentLines = Math.max(WELCOME_ANIMATION.frames[0].length, textLines.length);
114
+ const frameHeight = numContentLines + 1; // internal newlines (11) + trailing newlines (2) = 13
115
+ // Total height including initial newline (for cleanup)
116
+ const totalHeight = frameHeight + 1; // 14
117
+ // Initial render
118
+ process.stdout.write('\n');
119
+ // Animation loop
120
+ const interval = setInterval(() => {
121
+ if (!running)
122
+ return;
123
+ const frame = WELCOME_ANIMATION.frames[frameIndex];
124
+ // Move cursor up to overwrite previous frame (always after first render)
125
+ if (!isFirstRender) {
126
+ process.stdout.write(`\x1b[${frameHeight}A`);
127
+ }
128
+ isFirstRender = false;
129
+ // Render current frame
130
+ process.stdout.write(renderFrame(frame, textLines) + '\n\n');
131
+ // Advance to next frame
132
+ frameIndex = (frameIndex + 1) % WELCOME_ANIMATION.frames.length;
133
+ }, WELCOME_ANIMATION.interval);
134
+ // Wait for Enter
135
+ await waitForEnter();
136
+ // Stop animation
137
+ running = false;
138
+ clearInterval(interval);
139
+ // Clear the welcome screen and move on
140
+ process.stdout.write(`\x1b[${totalHeight}A`);
141
+ for (let i = 0; i < totalHeight; i++) {
142
+ process.stdout.write('\x1b[2K\n'); // Clear line
143
+ }
144
+ process.stdout.write(`\x1b[${totalHeight}A`); // Move back up
145
+ }
146
+ //# sourceMappingURL=welcome-screen.js.map
@@ -22,4 +22,15 @@ export declare class FileSystemUtils {
22
22
  static updateFileWithMarkers(filePath: string, content: string, startMarker: string, endMarker: string): Promise<void>;
23
23
  static ensureWritePermissions(dirPath: string): Promise<boolean>;
24
24
  }
25
+ /**
26
+ * Removes a marker block from file content.
27
+ * Only removes markers that are on their own lines (ignores inline mentions).
28
+ * Cleans up double blank lines that may result from removal.
29
+ *
30
+ * @param content - File content with markers
31
+ * @param startMarker - The start marker string
32
+ * @param endMarker - The end marker string
33
+ * @returns Content with marker block removed, or original content if markers not found/invalid
34
+ */
35
+ export declare function removeMarkerBlock(content: string, startMarker: string, endMarker: string): string;
25
36
  //# sourceMappingURL=file-system.d.ts.map
@@ -204,9 +204,28 @@ export class FileSystemUtils {
204
204
  }
205
205
  return await this.ensureWritePermissions(parentDir);
206
206
  }
207
- const testFile = path.join(dirPath, '.openspec-test-' + Date.now());
207
+ const testFile = path.join(dirPath, '.openspec-test-' + Date.now() + '-' + Math.random().toString(36).slice(2));
208
208
  await fs.writeFile(testFile, '');
209
- await fs.unlink(testFile);
209
+ // On Windows, file may be temporarily locked by antivirus or indexing services.
210
+ // Retry unlink with a small delay if it fails.
211
+ const maxRetries = 3;
212
+ for (let attempt = 0; attempt < maxRetries; attempt++) {
213
+ try {
214
+ await fs.unlink(testFile);
215
+ break;
216
+ }
217
+ catch (unlinkError) {
218
+ if (attempt === maxRetries - 1) {
219
+ // Last attempt failed, but we successfully wrote the file, so permissions are OK
220
+ // Just log and continue - the temp file will be cleaned up eventually
221
+ console.debug(`Could not clean up test file ${testFile}: ${unlinkError.message}`);
222
+ }
223
+ else {
224
+ // Wait briefly before retrying (Windows file lock release)
225
+ await new Promise((resolve) => setTimeout(resolve, 50));
226
+ }
227
+ }
228
+ }
210
229
  return true;
211
230
  }
212
231
  catch (error) {
@@ -215,4 +234,48 @@ export class FileSystemUtils {
215
234
  }
216
235
  }
217
236
  }
237
+ /**
238
+ * Removes a marker block from file content.
239
+ * Only removes markers that are on their own lines (ignores inline mentions).
240
+ * Cleans up double blank lines that may result from removal.
241
+ *
242
+ * @param content - File content with markers
243
+ * @param startMarker - The start marker string
244
+ * @param endMarker - The end marker string
245
+ * @returns Content with marker block removed, or original content if markers not found/invalid
246
+ */
247
+ export function removeMarkerBlock(content, startMarker, endMarker) {
248
+ const startIndex = findMarkerIndex(content, startMarker);
249
+ const endIndex = startIndex !== -1
250
+ ? findMarkerIndex(content, endMarker, startIndex + startMarker.length)
251
+ : findMarkerIndex(content, endMarker);
252
+ if (startIndex === -1 || endIndex === -1 || endIndex <= startIndex) {
253
+ return content;
254
+ }
255
+ // Find the start of the line containing the start marker
256
+ let lineStart = startIndex;
257
+ while (lineStart > 0 && content[lineStart - 1] !== '\n') {
258
+ lineStart--;
259
+ }
260
+ // Find the end of the line containing the end marker
261
+ let lineEnd = endIndex + endMarker.length;
262
+ while (lineEnd < content.length && content[lineEnd] !== '\n') {
263
+ lineEnd++;
264
+ }
265
+ // Include the trailing newline if present
266
+ if (lineEnd < content.length && content[lineEnd] === '\n') {
267
+ lineEnd++;
268
+ }
269
+ const before = content.substring(0, lineStart);
270
+ const after = content.substring(lineEnd);
271
+ // Clean up double blank lines (handle both Unix \n and Windows \r\n)
272
+ let result = before + after;
273
+ result = result.replace(/(\r?\n){3,}/g, '\n\n');
274
+ // Trim trailing whitespace but preserve leading whitespace and original newline style
275
+ if (result.trimEnd() === '') {
276
+ return '';
277
+ }
278
+ const newline = content.includes('\r\n') ? '\r\n' : '\n';
279
+ return result.trimEnd() + newline;
280
+ }
218
281
  //# sourceMappingURL=file-system.js.map
@@ -1,4 +1,5 @@
1
1
  export { validateChangeName, createChange } from './change-utils.js';
2
2
  export type { ValidationResult, CreateChangeOptions } from './change-utils.js';
3
3
  export { readChangeMetadata, writeChangeMetadata, resolveSchemaForChange, validateSchemaName, ChangeMetadataError, } from './change-metadata.js';
4
+ export { FileSystemUtils, removeMarkerBlock } from './file-system.js';
4
5
  //# sourceMappingURL=index.d.ts.map
@@ -2,4 +2,6 @@
2
2
  export { validateChangeName, createChange } from './change-utils.js';
3
3
  // Change metadata utilities
4
4
  export { readChangeMetadata, writeChangeMetadata, resolveSchemaForChange, validateSchemaName, ChangeMetadataError, } from './change-metadata.js';
5
+ // File system utilities
6
+ export { FileSystemUtils, removeMarkerBlock } from './file-system.js';
5
7
  //# sourceMappingURL=index.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fission-ai/openspec",
3
- "version": "0.23.0",
3
+ "version": "1.0.0",
4
4
  "description": "AI-native system for spec-driven development",
5
5
  "keywords": [
6
6
  "openspec",
@@ -1,17 +0,0 @@
1
- /**
2
- * Artifact Workflow CLI Commands (Experimental)
3
- *
4
- * This file contains all artifact workflow commands in isolation for easy removal.
5
- * Commands expose the ArtifactGraph and InstructionLoader APIs to users and agents.
6
- *
7
- * To remove this feature:
8
- * 1. Delete this file
9
- * 2. Remove the registerArtifactWorkflowCommands() call from src/cli/index.ts
10
- */
11
- import type { Command } from 'commander';
12
- /**
13
- * Registers all artifact workflow commands on the given program.
14
- * All commands are marked as experimental in their help text.
15
- */
16
- export declare function registerArtifactWorkflowCommands(program: Command): void;
17
- //# sourceMappingURL=artifact-workflow.d.ts.map